Skip to content

Commit 4e7646b

Browse files
committed
[Sort] QuickSort
1 parent 8a4cbcb commit 4e7646b

File tree

3 files changed

+260
-0
lines changed

3 files changed

+260
-0
lines changed

notes/README.md

+4
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,7 @@
1717
1. [그래프(Graph)](./Graph/Graph.md)
1818
2. [서로소 집합(Disjoint Set)과 Union-Find 알고리즘](./Graph/DisjointSet.md)
1919
3. [DAG(Directed Acyclic Graph)와 위상정렬(Topological Sort)](./Graph/TopologicalSort.md)
20+
21+
## 📖 Sort
22+
23+
1. [퀵정렬(QuickSort)](../src/Sort/QuickSort)

src/Sort/QuickSort/Main.java

+220
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
package Sort.QuickSort;
2+
3+
import java.util.Arrays;
4+
import java.util.Random;
5+
6+
public class Main {
7+
8+
public static void main(String[] args) {
9+
Random random = new Random();
10+
int n = 100000000;
11+
int[] arr = new int[n];
12+
for (int i = 0; i < n; i++) {
13+
arr[i] = random.nextInt(n);
14+
}
15+
16+
System.out.println("n : " + n);
17+
System.out.println("====== choice of pivot 2.1 ======");
18+
long start = System.currentTimeMillis();
19+
QuickSort qs1 = new QuickSort(arr, n);
20+
long end = System.currentTimeMillis();
21+
System.out.println( "실행 시간 : " + ( end - start )/1000.0 + "초");
22+
23+
System.out.println("====== choice of pivot 2.2 ======");
24+
start = System.currentTimeMillis();
25+
QuickSort2 qs2 = new QuickSort2(arr, n);
26+
end = System.currentTimeMillis();
27+
System.out.println( "실행 시간 : " + ( end - start )/1000.0 + "초");
28+
29+
System.out.println("====== choice of pivot 2.3 ======");
30+
start = System.currentTimeMillis();
31+
QuickSort3 qs3 = new QuickSort3(arr, n);
32+
end = System.currentTimeMillis();
33+
System.out.println( "실행 시간 : " + ( end - start )/1000.0 + "초");
34+
}
35+
}
36+
37+
// The first element is the pivot
38+
class QuickSort {
39+
40+
int[] S;
41+
int comparison_count = 0;
42+
int exchange_count = 0;
43+
44+
public QuickSort(int[] arr, int n) {
45+
this.S = new int[n];
46+
System.arraycopy(arr, 0, this.S, 0, n);
47+
48+
// System.out.println(Arrays.toString(this.S));
49+
sort(0, n-1);
50+
// System.out.println(Arrays.toString(this.S));
51+
52+
System.out.println("Comparison : " + this.comparison_count);
53+
System.out.println("Exchange : " + this.exchange_count);
54+
}
55+
56+
void sort (int low, int high) {
57+
if (low < high) {
58+
int pivotPoint = partition(low, high);
59+
sort(low, pivotPoint-1);
60+
sort(pivotPoint+1, high);
61+
}
62+
}
63+
64+
int partition(int low, int high) {
65+
int pivot = this.S[low];
66+
int j = low;
67+
for (int i = low+1; i <= high; i++) {
68+
if (this.S[i] < pivot) {
69+
this.comparison_count ++;
70+
j++;
71+
swap(i, j);
72+
}
73+
}
74+
swap(low, j);
75+
return j;
76+
}
77+
78+
void swap(int a, int b) {
79+
this.exchange_count ++;
80+
int temp = this.S[a];
81+
this.S[a] = this.S[b];
82+
this.S[b] = temp;
83+
}
84+
}
85+
86+
// A randomly chosen element between low and high is the pivot.
87+
class QuickSort2 {
88+
89+
int[] S;
90+
int comparison_count = 0;
91+
int exchange_count = 0;
92+
93+
public QuickSort2(int[] arr, int n) {
94+
this.S = new int[n];
95+
System.arraycopy(arr, 0, this.S, 0, n);
96+
97+
// System.out.println(Arrays.toString(this.S));
98+
sort2(0, n-1);
99+
// System.out.println(Arrays.toString(this.S));
100+
101+
System.out.println("Comparison : " + this.comparison_count);
102+
System.out.println("Exchange : " + this.exchange_count);
103+
}
104+
105+
void sort2 (int low, int high) {
106+
if (low < high) {
107+
int pivotPoint = partition2(low, high);
108+
sort2(low, pivotPoint-1);
109+
sort2(pivotPoint+1, high);
110+
}
111+
}
112+
113+
int partition2(int low, int high) {
114+
Random random = new Random();
115+
int num = random.nextInt(2);
116+
117+
int pivot;
118+
if (num == 0) {
119+
pivot = this.S[low];
120+
} else {
121+
pivot = this.S[high];
122+
swap(low, high);
123+
}
124+
125+
int j = low;
126+
for (int i = low+1; i <= high; i++) {
127+
if (this.S[i] < pivot) {
128+
this.comparison_count ++;
129+
j++;
130+
swap(i, j);
131+
}
132+
}
133+
swap(low, j);
134+
return j;
135+
}
136+
137+
void swap(int a, int b) {
138+
this.exchange_count ++;
139+
int temp = this.S[a];
140+
this.S[a] = this.S[b];
141+
this.S[b] = temp;
142+
}
143+
}
144+
145+
// The pivot is chosen as the median number, as suggested by Robert Sedgewick,
146+
// among the first, the last, and the mid elements between low and high.
147+
class QuickSort3 {
148+
149+
int[] S;
150+
int comparison_count = 0;
151+
int exchange_count = 0;
152+
153+
public QuickSort3(int[] arr, int n) {
154+
this.S = new int[n];
155+
System.arraycopy(arr, 0, this.S, 0, n);
156+
157+
// System.out.println(Arrays.toString(this.S));
158+
sort3(0, n-1);
159+
// System.out.println(Arrays.toString(this.S));
160+
161+
System.out.println("Comparison : " + this.comparison_count);
162+
System.out.println("Exchange : " + this.exchange_count);
163+
}
164+
165+
void sort3 (int low, int high) {
166+
if (low < high) {
167+
int pivotPoint = partition3(low, high);
168+
sort3(low, pivotPoint-1);
169+
sort3(pivotPoint+1, high);
170+
}
171+
}
172+
173+
int partition3(int low, int high) {
174+
int mid = (low + high) / 2;
175+
int num = getMedian(low, mid, high);
176+
177+
int pivot;
178+
if (num == low) {
179+
pivot = this.S[low];
180+
} else if (num == mid) {
181+
pivot = this.S[mid];
182+
swap(low, mid);
183+
} else {
184+
pivot = this.S[high];
185+
swap(low, high);
186+
}
187+
188+
int j = low;
189+
for (int i = low+1; i <= high; i++) {
190+
if (this.S[i] < pivot) {
191+
this.comparison_count ++;
192+
j++;
193+
swap(i, j);
194+
}
195+
}
196+
swap(low, j);
197+
return j;
198+
}
199+
200+
int getMedian(int a, int b, int c) {
201+
if (this.S[a] >= this.S[b]) {
202+
if (this.S[b] >= this.S[c]) return b;
203+
else if (this.S[a] <= this.S[c]) return a;
204+
else return c;
205+
} else if (this.S[a] > this.S[c]) {
206+
return a;
207+
} else if (this.S[b] > this.S[c]) {
208+
return c;
209+
} else {
210+
return b;
211+
}
212+
}
213+
214+
void swap(int a, int b) {
215+
this.exchange_count ++;
216+
int temp = this.S[a];
217+
this.S[a] = this.S[b];
218+
this.S[b] = temp;
219+
}
220+
}

src/Sort/QuickSort/README.md

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Sort - QuickSort
2+
3+
## QuickSort란
4+
5+
QuickSort는 pivot을 설정하여 그 값을 기준으로 정렬한다.
6+
추가 메모리 공간을 최대한 활용하지 않은 것이 특징이다.
7+
재귀 함수를 활용하기에 엄밀하게 이야기하면 In-place sort 라고 할 수 없지만, 그에 가깝다고 할 수 있다.
8+
Worst-case Time Complexity는 O(n^2)이지만, Worst-case가 매우 극단적인 경우임을 감안한다면
9+
Average-case Time Complexity를 계산하는 것이 훨씬 유의미할 것이고, 이는 O(nlgn)이다.
10+
Merge sort와 같은 수준의 Time Complexity를 가지며 Space Complexity 측면에서도 좋은 성능을 가지니
11+
QuickSort는 참 좋은 알고리즘 !!
12+
13+
## pivot 선택 방법
14+
15+
QuickSort에서 pivot을 설정하는 방법은 다음과 같은 3가지가 있다.
16+
17+
1. 첫 번째 element(`low`)가 pivot이다.
18+
2. `low`, `high` 값 중 무작위로 선택한 값이 pivot이다.
19+
3. `low`, `high`, `mid` 값 중 중간값(median value)이 pivot이다. (Robert Sedgewick에 의해 제안됨)
20+
21+
3가지 방법 각각에 대하여 `QuickSort`, `QuickSort2`, `QuickSort3` class로 구현해보았다.
22+
그리고 Comparison, Exchange 연산 횟수와 실행 시간을 비교해 본 결과 다음과 같은 그래프를 도출할 수 있었다.
23+
24+
Comparison
25+
26+
![image](https://user-images.githubusercontent.com/22045163/95844387-d5932800-0d83-11eb-9f90-cbe70f2f6e31.png)
27+
28+
Exchange
29+
30+
![image](https://user-images.githubusercontent.com/22045163/95844415-dcba3600-0d83-11eb-8372-04c6078a8b8e.png)
31+
32+
Time
33+
34+
![image](https://user-images.githubusercontent.com/22045163/95844441-e348ad80-0d83-11eb-92b7-3f3391413918.png)
35+
36+
3번째 pivot 선택 방법이 가장 좋은 성능을 가짐을 알 수 있다. (Median of Three)

0 commit comments

Comments
 (0)