Skip to content

Commit e9f6955

Browse files
author
gyusuk
committed
first commit
1 parent c84b5a0 commit e9f6955

17 files changed

+2738
-0
lines changed

Algorithm/HeapSort.md

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
#### 힙 소트(Heap Sort)
2+
3+
---
4+
5+
6+
7+
완전 이진 트리를 기본으로 하는 힙(Heap) 자료구조를 기반으로한 정렬 방식
8+
9+
***완전 이진 트리란?***
10+
11+
> 삽입할 때 왼쪽부터 차례대로 추가하는 이진 트리
12+
13+
14+
15+
힙 소트는 `불안정 정렬`에 속함
16+
17+
18+
19+
**시간복잡도**
20+
21+
| 평균 | 최선 | 최악 |
22+
| :------: | :------: | :------: |
23+
| O(nlogn) | O(nlogn) | O(nlogn) |
24+
25+
26+
27+
##### 과정
28+
29+
1. 최대 힙을 구성
30+
2. 현재 힙 루트는 가장 큰 값이 존재함. 루트의 값을 마지막 요소와 바꾼 후, 힙의 사이즈 하나 줄임
31+
3. 힙의 사이즈가 1보다 크면 위 과정 반복
32+
33+
34+
35+
<img src="https://t1.daumcdn.net/cfile/tistory/999896445AD4953023">
36+
37+
루트를 마지막 노드로 대체 (11 → 4), 다시 최대 힙 구성
38+
39+
<img src="https://t1.daumcdn.net/cfile/tistory/99E1AD445AD4953015">
40+
41+
42+
43+
이와 같은 방식으로 최대 값을 하나씩 뽑아내면서 정렬하는 것이 힙 소트
44+
45+
46+
47+
```
48+
public void heapSort(int[] array) {
49+
int n = array.length;
50+
51+
// max heap 초기화
52+
for (int i = n/2-1; i>=0; i--){
53+
heapify(array, n, i); // 1
54+
}
55+
56+
// extract 연산
57+
for (int i = n-1; i>0; i--) {
58+
swap(array, 0, i);
59+
heapify(array, i, 0); // 2
60+
}
61+
}
62+
```
63+
64+
65+
66+
##### 1번째 heapify
67+
68+
> 일반 배열을 힙으로 구성하는 역할
69+
>
70+
> 자식노드로부터 부모노드 비교
71+
>
72+
>
73+
>
74+
> - *n/2-1부터 0까지 인덱스가 도는 이유는?*
75+
>
76+
> 부모 노드의 인덱스를 기준으로 왼쪽 자식노드 (i*2 + 1), 오른쪽 자식 노드(i*2 + 2)이기 때문
77+
78+
79+
80+
##### 2번째 heapify
81+
82+
> 요소가 하나 제거된 이후에 다시 최대 힙을 구성하기 위함
83+
>
84+
> 루트를 기준으로 진행(extract 연산 처리를 위해)
85+
86+
87+
88+
```
89+
public void heapify(int array[], int n, int i) {
90+
int p = i;
91+
int l = i*2 + 1;
92+
int r = i*2 + 2;
93+
94+
//왼쪽 자식노드
95+
if (l < n && array[p] < array[l]) {
96+
p = l;
97+
}
98+
//오른쪽 자식노드
99+
if (r < n && array[p] < array[r]) {
100+
p = r;
101+
}
102+
103+
//부모노드 < 자식노드
104+
if(i != p) {
105+
swap(array, p, i);
106+
heapify(array, n, p);
107+
}
108+
}
109+
```
110+
111+
**다시 최대 힙을 구성할 때까지** 부모 노드와 자식 노드를 swap하며 재귀 진행
112+
113+
114+
115+
퀵정렬과 합병정렬의 성능이 좋기 때문에 힙 정렬의 사용빈도가 높지는 않음.
116+
117+
하지만 힙 자료구조가 많이 활용되고 있으며, 이때 함께 따라오는 개념이 `힙 소트`
118+
119+
120+
121+
##### 힙 소트가 유용할 때
122+
123+
- 가장 크거나 가장 작은 값을 구할 때
124+
125+
> 최소 힙 or 최대 힙의 루트 값이기 때문에 한번의 힙 구성을 통해 구하는 것이 가능
126+
127+
- 최대 k 만큼 떨어진 요소들을 정렬할 때
128+
129+
> 삽입정렬보다 더욱 개선된 결과를 얻어낼 수 있음
130+
131+
132+
133+
##### 전체 소스 코드
134+
135+
```
136+
private void solve() {
137+
int[] array = { 230, 10, 60, 550, 40, 220, 20 };
138+
139+
heapSort(array);
140+
141+
for (int v : array) {
142+
System.out.println(v);
143+
}
144+
}
145+
146+
public static void heapify(int array[], int n, int i) {
147+
int p = i;
148+
int l = i * 2 + 1;
149+
int r = i * 2 + 2;
150+
151+
if (l < n && array[p] < array[l]) {
152+
p = l;
153+
}
154+
155+
if (r < n && array[p] < array[r]) {
156+
p = r;
157+
}
158+
159+
if (i != p) {
160+
swap(array, p, i);
161+
heapify(array, n, p);
162+
}
163+
}
164+
165+
public static void heapSort(int[] array) {
166+
int n = array.length;
167+
168+
// init, max heap
169+
for (int i = n / 2 - 1; i >= 0; i--) {
170+
heapify(array, n, i);
171+
}
172+
173+
// for extract max element from heap
174+
for (int i = n - 1; i > 0; i--) {
175+
swap(array, 0, i);
176+
heapify(array, i, 0);
177+
}
178+
}
179+
180+
public static void swap(int[] array, int a, int b) {
181+
int temp = array[a];
182+
array[a] = array[b];
183+
array[b] = temp;
184+
}
185+
```
186+

Algorithm/MergeSort.md

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#### 머지 소트(Merge Sort)
2+
3+
---
4+
5+
6+
7+
합병 정렬이라고도 부르며, 분할 정복 방법을 통해 구현
8+
9+
***분할 정복이란?***
10+
11+
> 큰 문제를 작은 문제 단위로 쪼개면서 해결해나가는 방식
12+
13+
14+
15+
빠른 정렬로 분류되며, 퀵소트와 함께 많이 언급되는 정렬 방식이다.
16+
17+
18+
19+
퀵소트와는 반대로 `안정 정렬`에 속함
20+
21+
**시간복잡도**
22+
23+
| 평균 | 최선 | 최악 |
24+
| :------: | :------: | :------: |
25+
| O(nlogn) | O(nlogn) | O(nlogn) |
26+
27+
요소를 쪼갠 후, 다시 합병시키면서 정렬해나가는 방식으로, 쪼개는 방식은 퀵정렬과 유사
28+
29+
30+
31+
- mergeSort
32+
33+
```
34+
public void mergeSort(int[] array, int left, int right) {
35+
36+
if(left < right) {
37+
int mid = (left + right) / 2;
38+
39+
mergeSort(array, left, mid);
40+
mergeSort(array, mid+1, right);
41+
merge(array, left, mid, right);
42+
}
43+
44+
}
45+
```
46+
47+
정렬 로직에 있어서 merge() 메소드가 핵심
48+
49+
50+
51+
*퀵소트와의 차이점*
52+
53+
> 퀵정렬 : 우선 피벗을 통해 정렬(partition) → 영역을 쪼갬(quickSort)
54+
>
55+
> 합병정렬 : 영역을 쪼갤 수 있을 만큼 쪼갬(mergeSort) → 정렬(merge)
56+
57+
58+
59+
- merge()
60+
61+
```
62+
public static void merge(int[] array, int left, int mid, int right) {
63+
int[] L = Arrays.copyOfRange(array, left, mid + 1);
64+
int[] R = Arrays.copyOfRange(array, mid + 1, right + 1);
65+
66+
int i = 0, j = 0, k = left;
67+
int ll = L.length, rl = R.length;
68+
69+
while(i < ll && j < rl) {
70+
if(L[i] <= R[j]) {
71+
array[k] = L[i++];
72+
}
73+
else {
74+
array[k] = R[j++];
75+
}
76+
k++;
77+
}
78+
79+
// remain
80+
while(i < ll) {
81+
array[k++] = L[i++];
82+
}
83+
while(j < rl) {
84+
array[k++] = R[j++];
85+
}
86+
}
87+
```
88+
89+
이미 **합병의 대상이 되는 두 영역이 각 영역에 대해서 정렬이 되어있기 때문**에 단순히 두 배열을 **순차적으로 비교하면서 정렬할 수가 있다.**
90+
91+
92+
93+
94+
95+
**★★★합병정렬은 순차적**인 비교로 정렬을 진행하므로, **LinkedList의 정렬이 필요할 때 사용하면 효율적**이다.★★★
96+
97+
98+
99+
*LinkedList를 퀵정렬을 사용해 정렬하면?*
100+
101+
> 성능이 좋지 않음
102+
>
103+
> 퀵정렬은, 순차 접근이 아닌 **임의 접근이기 때문**
104+
105+
106+
107+
**LinkedList는 삽입, 삭제 연산에서 유용**하지만 **접근 연산에서는 비효율적**
108+
109+
따라서 임의로 접근하는 퀵소트를 활용하면 오버헤드 발생이 증가하게 됨
110+
111+
> 배열은 인덱스를 이용해서 접근이 가능하지만, LinkedList는 Head부터 탐색해야 함
112+
>
113+
> 배열[O(1)] vs LinkedList[O(n)]
114+
115+
116+
117+
118+
119+
```
120+
private void solve() {
121+
int[] array = { 230, 10, 60, 550, 40, 220, 20 };
122+
123+
mergeSort(array, 0, array.length - 1);
124+
125+
for (int v : array) {
126+
System.out.println(v);
127+
}
128+
}
129+
130+
public static void mergeSort(int[] array, int left, int right) {
131+
if (left < right) {
132+
int mid = (left + right) / 2;
133+
134+
mergeSort(array, left, mid);
135+
mergeSort(array, mid + 1, right);
136+
merge(array, left, mid, right);
137+
}
138+
}
139+
140+
public static void merge(int[] array, int left, int mid, int right) {
141+
int[] L = Arrays.copyOfRange(array, left, mid + 1);
142+
int[] R = Arrays.copyOfRange(array, mid + 1, right + 1);
143+
144+
int i = 0, j = 0, k = left;
145+
int ll = L.length, rl = R.length;
146+
147+
while (i < ll && j < rl) {
148+
if (L[i] <= R[j]) {
149+
array[k] = L[i++];
150+
} else {
151+
array[k] = R[j++];
152+
}
153+
k++;
154+
}
155+
156+
while (i < ll) {
157+
array[k++] = L[i++];
158+
}
159+
160+
while (j < rl) {
161+
array[k++] = R[j++];
162+
}
163+
}
164+
```
165+

0 commit comments

Comments
 (0)