heap - 이진트리의 활용
1. heap?
이 heap을 구현하는 데에도 Binary heap, Binomial heap, Fibonacci heap이(내가 아는 한도에서) 존재한다.
하지만 이번 글은 가장 내 수준에서 많이 쓰는 이진 힙(Binary heap)을 기준으로 설명하겠다.
이진 힙(Binary heap)은 기본적으로 완전이진트리 이여야 한다.
이진 힙(Binary heap)은 min-heap, max-heap으로 구별을 할 수 있을 것 같다.
min-heap : 부모노드의 값이 자식노드의 값보다 작다.
max-heap : 부모노드의 값이 자식노드의 값보다 크다.
이 2가지 조건만 만족하면서 트리를 만들게 되면 쉽게 heap을 구성할 수 있을 것이다.
간단하게 max-heap의 예를 하나 들어보겠다.
위의 트리를 보면
조건 1 : 완전이진 트리 => 만족한다.
조건 2 : 부모노드의 값이 자식노드의 값보다 크다
=> 8은 4와 6보다 크다.
=> 4는 2와 3보다 크다.
=> 만족한다.
따라서 위의 트리는 max-heap이라고 할 수가 있다.
이렇게 끝내기에는 너무 허무하고 뭐지라는 생각이 들수가 있다.
이 heap은 삽입과 삭제를 하는 과정에서 이진트리의 특성을 잘 살리기 때문에 삽입과 삭제에 대해서 알아보아야 한다.
1.1 삽입
동작 과정을 보면 알겠지만 내가 삽입을 한다고 했을 때 최대 이 트리의 높이만큼 비교를 해야한다.
트리의 높이는 트리의 노드의 수가 N이라고 할때 완전이진트리 이기 때문에 Log(N)이다.(높이 h일때 원소는 1+2+4+...+2^h-1)
따라서 삽입하는 과정에서의 복잡도는 O(logN) 이라고 볼수 있다.
자세한 증명은 생략하겠다.
1.2 삭제
2. heap의 구현
Binary heap의 구현은 그렇게 간단하지는 않다. 하지만 구현할줄 모르는 것은 모르는 것이기 떄문에 구현해보도록 하겠다.
다른 자료구조와 동일하게 C++ class로 구현을 해 보겠다.
직관적으로 구현을 한거라 소스가 이쁘게 나오지는 않았다 단순히 참고,공부용으로만 이용하길 바란다.
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | #include <iostream> using namespace std; //Max_heap class Heap { private: int* heap; int capacity; int currentSize; void swap(int* a, int *b) { int t = *a; *a = *b; *b = t; } public : //힙 생성자 Heap(int Capacity) : capacity(Capacity){ heap = new int[Capacity]; currentSize = 0; } //힙 파괴자 ~Heap() { delete[] heap; } //삽입 void Insert(int val) { //Heap Size Check if (currentSize >= capacity) { printf("Heap is full!"); return; } //마지막에 원소 삽입 int mPos = currentSize++; heap[mPos] = val; //부모값보다 내가 크다면 교환 교환 교환 int parPos = (mPos - 1) / 2; while (heap[mPos]>heap[parPos]) { swap(&heap[mPos], &heap[parPos]); mPos = parPos; parPos = (mPos - 1) / 2; } } //삭제, 추출 int pop() { if (currentSize <= 0) { printf("Heap is Empty!!!\n"); return NULL; } int returnVal = heap[0]; //1. root와 가장 마지막 노드 교환 swap(&heap[0],&heap[currentSize-1]); //2. 교환된 마지막 노드 삭제 heap[currentSize - 1] = NULL; currentSize--; //3. 새로운 root노드 자리 찾아가기. int root = 0; int leftChild; int rightChild; while (root<=currentSize-1) { leftChild = (root + 1) * 2 - 1; rightChild = (root + 1) * 2; if (leftChild >= currentSize) { //Child 존재 X break; } else if (rightChild >=currentSize) { //왼쪽 Child만 존재 if (heap[leftChild] > heap[root]) { swap(&heap[leftChild], &heap[root]); root = leftChild; } else break; } else { //양쪽 Child 존재 if (heap[leftChild] > heap[root] && heap[rightChild] > heap[root]) { if (heap[leftChild]<heap[rightChild]) { swap(&heap[rightChild], &heap[root]); root = rightChild; } else { swap(&heap[leftChild], &heap[root]); root = leftChild; } } else if (heap[leftChild] > heap[root]) { swap(&heap[leftChild], &heap[root]); root = leftChild; } else if (heap[rightChild] > heap[root]) { swap(&heap[rightChild], &heap[root]); root = rightChild; } else { break; } } } return returnVal; } int GetTop() { if(currentSize>=1) return heap[0]; else return -1; } }; int main() { Heap heap = Heap(100); for (int i = 1; i <= 8; i++) heap.Insert(i); for (int i = 1; i <= 8; i++) heap.Insert(8-i+1); for (int i = 1; i <= 16; i++) printf("%d ", heap.pop()); return 0; } | cs |
'알고리즘 > 이론' 카테고리의 다른 글
그래프(Graph)의 정의와 표현 (2) | 2016.09.19 |
---|---|
이진트리(Binary Tree) - 트리(Tree)의 기초 (1) | 2016.09.13 |
이진탐색(Binary Search)와 Parametric Search (2) | 2016.09.13 |
퀵소트(quick sort) 알고리즘 (4) | 2016.09.08 |
네트워크플로우(Network flow) - 포드 풀커슨(Ford-Fulkerson) 알고리즘 (3) | 2016.07.28 |