Heap정렬을 하기 전에... Heap이란?
- 여러 개의 값들 중, 가장 큰 값이나 가장 작은 값을 빠르게 찾아내도록 만들어진 자료 구조.
- 부모 노드의 Key >= 자식 노드의 Key : MAX Heap
- 부모 노드의 Key <= 자식 노드의 Key : MIN Heap
* Heap의 목적
* Heap의 구현
- 배열로 구현한다.
- root 노드를 1번 배열이라고 가정 했을 때, 다음 그림처럼 순서대로 배열에 정렬 시킬 수 있다.
- 현재의 노드를 기준으로
1) 부모의 노드를 알고 싶다면 : (현재 노드의 인덱스) / 2
2) 왼쪽 자식의 노드를 알고 싶다면 : (현재 노드) * 2
3) 오른쪽 자식의 노드를 알고 싶다면 : (현재 노드) * 2 + 1
[삽입 알고리즘]
1) Heap의 끝에 새로운 노드를 삽입한다.
2) 삽입된 노드와 부모 노드 키 값 비교(MAX Heap 기준)
if ) 삽입 노드의 키 값 > 부모 노드의 키 값 : 삽입 노드와 부모 노드 Swap
else ) 종료
3) 삽입 노드로 계속 2번을 종료될 때까지 반복.
[Code]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include<stdio.h> #include<stdlib.h> typedef struct _Heap{ int heap[200], heap_size; }H; H heap1; void Swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } void insert_max_heap(int item) { int idx = ++heap1.heap_size; heap1.heap[idx] = item; while ((idx > 0) && (heap1.heap[idx / 2] < heap1.heap[idx])) { Swap(&heap1.heap[idx / 2], &heap1.heap[idx]); idx /= 2; } } | cs |
[삭제 알고리즘]
1. 루트 노드를 삭제한다. (루트 노드는 MAX Heap에서 최대, MIN Heap에서 최소)
2. 삭제된 루트 노드에는 히프의 마지막 노드를 가져다 놓는다.
3. MAX Heap이라고 가정하고 시작하자.
1) 자신의 왼쪽 노드와 값을 비교한다.
if ) 자신이 더 크다
- 오른쪽 자식과 비교.
if ) 오른쪽 자식보다도 자신이 크다 : 오른쪽 노드와 Swap
else ) 오른쪽 자식보다는 작다 : 왼쪽 노드와 Swap
else ) 자신이 더 작다 : 종료
4. 3번이 끝날 때까지 반복.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | int delete_max_heap() { int root_node = heap1.heap[1]; heap1.heap[1] = heap1.heap[heap1.heap_size--]; int parent = 1, child = 2; while (child <= heap1.heap_size) { // child가 히프의 마지막 노드가 될 수도 있으므로 첫 번째 조건 필수. // 왼쪽 자식노드와 오른쪽 자식노드 비교, 더 큰 노드를 타겟으로 잡음. if (child < heap1.heap_size && heap1.heap[child] < heap1.heap[child + 1]) child++; // 자식 노드 중 가장 큰 노드와 부모 노드 비교시, 부모노드가 크거나 같으면 종료 if (heap1.heap[parent] >= heap1.heap[child]) break; // 부모 노드와 자식 노드 교체 Swap(&heap1.heap[parent], &heap1.heap[child]); // 인덱스 재설정 parent = child; child *= 2; } return root_node; } | cs |
1. Heap Sort
- 굳이 전체를 전부 정렬할 필요 없이, 가장 큰(or 가장 작은) 원소부터 차례로 추출할 수 있는 Heap의 장점을 이용한 Sort
- 오름 차순으로 정렬시에는 MIN Heap, 내림 차순으로 정렬시에는 MAX Heap을 구성하면 된다.
- 시간 복잡도는 O(nlog2n)
- 주로 Priority Queue(우선 순위 큐)를 만들 때 자주 쓰인다.
2. Quick Sort
[정렬 메커니즘]
[Code]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | void Quick_Sort(int *arr, int left, int right) { // 인덱스 교차시 재귀에서 벗어남 if (left >= right) return; // pivot은 배열 오른쪽 끝으로 설정. int pivot = right; // l은 left, r은 피봇 바로 왼쪽으로 설정. int l = left; int r = right - 1; while (l < r) { // left는 피봇보다 큰 놈이 나올 때까지 증가 while (l < r && arr[pivot] > arr[l]) l++; // right는 피봇보다 작은 놈이 나올 때까지 감소 while (l < r && arr[pivot] < arr[r]) r--; // 두 인덱스가 교차하지 않았는지 체크한 후, left와 right의 값 Swap if (l < r) Swap(&arr[l], &arr[r]); } // 위에서 정렬이 다 끝나면 pivot과 교차지점 Swap Swap(&arr[pivot], &arr[l]); // 재귀로 이분할 Quick_Sort(arr, left, l - 1); Quick_Sort(arr, l + 1, right); } | cs |
[장점]
- 타 정렬보다 속도가 빠르다.
- 추가 메모리 공간을 필요로 하지 않는다.
[단점]
- 그러나, 이미 정렬이 된 배열을 Sorting할 때에는 O(n^2)의 최악의 복잡도가 발생할 수 있다는 치명적인 단점을 갖고 있다.
[좀 더 효율적인 Quick Sort]
- 단순히 pivot을 왼쪽, 가운데, 오른쪽 혹은 랜덤으로 지정하는 방법보다 훨씬 효율적으로 pivot을 설정하는 방법으로 중간값 Quick Sort가 있다.
- 일반적으로 리스트의 왼쪽, 가운데, 오른쪽 3개의 데이터 중, 크기 순으로 중간 값을 설정하는 방법(Median of Three Quick Sort)가 있다.
[정렬 메커니즘]
'(구) 자료 > C' 카테고리의 다른 글
[1-5] namespace란? (0) | 2018.08.27 |
---|---|
[1-4] inline 함수 (0) | 2018.08.27 |
[8] Binary Search (0) | 2018.07.19 |
[6] Queue (0) | 2018.05.07 |
[5] Stack (0) | 2018.05.06 |