자료구조

힙(Heap)

웜블 2021. 12. 28. 23:57

힙(Heap)?

'무엇인가를 쌓아올린 더미'라는 뜻을 가진 영단어입니다. 힙 트리를 줄여 힙이라 부릅니다.

이 의미를 잘 기억해두고, 힙이 어떤 형태와 특성을 가지고 있기에 이런 이름이 붙여졌는지 알아보겠습니다.

 

특징

  • 힙의 목적은 '최대값 또는 최소값을 빠르게 찾기'
  • 최대 힙과 최소 힙 2 종류가 있습니다.
    최대 힙은 부모 노드의 값이 자식 노드 보다 항상 큰 힙이고, 최소 힙은 부모 노드의 값이 자식 노드 보다 항상 작은 힙입니다.
  • 루트 노드에는 항상 최대값 또는 최소값이 저장되어 있어 최대값/최소값을 한 번에 찾을 수 있습니다.O(1)

 

시간 복잡도

최대값/최소값 검색: O(1)

삽입/삭제: O(logn)

 

용도

  • 우선순위 큐(priority queue) 구현
  • 힙 정렬(heap sort) 구현

 

구현

최대 힙 구현
class MaxHeap {
    constructor() {
        // index 계산을 간편하게 하기 위해 index=1 부터 시작
        this.arr = [null];
    }
    // 삽입
    insert(data) {
        this.arr.push(data);
        // 삽입한 데이터의 index
        let index = this.arr.length - 1;
        // 1이 되면 벗어난다.
        while (index > 1) {
            // 부모 노드의 index
            let parentIndex = Math.floor(index / 2);
            if (this.arr[parentIndex] < this.arr[index]) {
                // 부모 노드보다 크면 swap
                let temp = this.arr[parentIndex];
                this.arr[parentIndex] = this.arr[index];
                this.arr[index] = temp;

                index = parentIndex;
            } else {
                break;
            }
        }
    }
    // 루트 노드(최대값) 삭제
    delete() {
        // heap 사이즈가 1이하이면(비어있으면) null 반환 후 종료
        if (this.arr.length <= 1) {
            return "힙이 비었습니다.";
        }
        // 말단 노드와 swap
        const temp = this.arr[1];
        this.arr[1] = this.arr[this.arr.length - 1];
        this.arr[this.arr.length - 1] = temp;
        const removed = this.arr.pop();

        let index = 1;
        while (true) {
            let leftChildIndex = index * 2;
            let rightChildIndex = index * 2 + 1;
            let swapIndex = null;
            
            // 왼쪽, 오른쪽 자식 순으로 비교 후 더 큰 수의 index 저장
            if (this.arr[index] < this.arr[leftChildIndex]) {
                swapIndex = leftChildIndex;
            }
            if (this.arr[index] < this.arr[rightChildIndex]) {
                swapIndex = rightChildIndex;
            }
            // 저장된 swap index가 없을 경우, swap 종료;
            if (swapIndex === null) {
                break;
            }
            // swap
            let temp = this.arr[index];
            this.arr[index] = this.arr[swapIndex];
            this.arr[swapIndex] = temp;
        }
        return removed;
    }
}
let maxHeap = new MaxHeap();
maxHeap.insert(1);
maxHeap.insert(2);
maxHeap.insert(3);
maxHeap.insert(4);
maxHeap.insert(5);
maxHeap.insert(6);
maxHeap.insert(7);
console.log(maxHeap.delete());
console.log(maxHeap.delete());
console.log(maxHeap.delete());
console.log(maxHeap.delete());
console.log(maxHeap.delete());
console.log(maxHeap.delete());

/* 실행 결과
7
6
5
4
3
2
*/

 

Reference