
🤔python으로 코딩 테스트 문제풀이를 하면 heapq를 활용해서 쉽게 heap 자료구조를 사용할 수 있다.
👀하지만 js에는 내장된 힙 자료구조가 없다.
💡다익스트라 문제 풀이를 하던 도중, 최소 힙을 활용하고 싶어서 js로 힙 class 구현을 정리해보았다.
class MinHeap {
constructor() {
this.heap = [ null ];
}
size() {
return this.heap.length - 1;
}
getMin() {
return this.heap[1] ? this.heap[1] : null;
}
swap(a, b) {
[ this.heap[a], this.heap[b] ] = [ this.heap[b], this.heap[a] ];
}
heappush(value) {
this.heap.push(value);
let curIdx = this.heap.length - 1;
let parIdx = (curIdx / 2) >> 0;
while(curIdx > 1 && this.heap[parIdx] > this.heap[curIdx]) {
this.swap(parIdx, curIdx)
curIdx = parIdx;
parIdx = (curIdx / 2) >> 0;
}
}
heappop() {
const min = this.heap[1];
if(this.heap.length <= 2) this.heap = [ null ];
else this.heap[1] = this.heap.pop();
let curIdx = 1;
let leftIdx = curIdx * 2;
let rightIdx = curIdx * 2 + 1;
if(!this.heap[leftIdx]) return min;
if(!this.heap[rightIdx]) {
if(this.heap[leftIdx] < this.heap[curIdx]) {
this.swap(leftIdx, curIdx);
}
return min;
}
while(this.heap[leftIdx] < this.heap[curIdx] || this.heap[rightIdx] < this.heap[curIdx]) {
const minIdx = this.heap[leftIdx] > this.heap[rightIdx] ? rightIdx : leftIdx;
this.swap(minIdx, curIdx);
curIdx = minIdx;
leftIdx = curIdx * 2;
rightIdx = curIdx * 2 + 1;
}
return min;
}
}
삽입과 삭제에 O(logN)의 시간 소요.최대힙은 부모 노드의 값이 자식 노드의 값보다 큰 힙.최소힙은 반대로 부모노드의 값이 자식 노드의 값보다 작은 힙.느슨한 완전이진트리이기 때문에 배열로 쉽게 구현 가능.
포화 (Full) 이진트리 : 이진트리이면서 서브트리까지 모두 빈곳이 없이 꽉찬 트리.
완전(Complete) 이진트리 : 높이가 h 일 때 레벨 h-1까지는 포화 이진트리고, 레벨 h에서는 왼쪽부터 노드가 순서대로 채워진 이진트리

class MinHeap {
constructor() {
this.heap = [ null ];
}
}
null로 채우기size() {
return this.heap.length - 1;
}
getMin() {
return this.heap[1] ? this.heap[1] : null;
}
heap[1]을 리턴해주기. 빈 힙이라면 null을 리턴하기. swap(a, b) {
[ this.heap[a], this.heap[b] ] = [ this.heap[b], this.heap[a] ];
}
heappush(value) {
this.heap.push(value);
let curIdx = this.heap.length - 1;
let parIdx = (curIdx / 2) >> 0;
while(curIdx > 1 && this.heap[parIdx] > this.heap[curIdx]) {
this.swap(parIdx, curIdx)
curIdx = parIdx;
parIdx = (curIdx / 2) >> 0;
}
}
heappop() {
// 배열 첫 원소는 비워두기, root 는 항상 heap[1]에 위치
const min = this.heap[1];
if(this.heap.length <= 2) this.heap = [ null ];
// 배열 마지막 원소를 root에 먼저 두기
else this.heap[1] = this.heap.pop();
let curIdx = 1; //현재 노드
let leftIdx = curIdx * 2; //왼쪽 노드
let rightIdx = curIdx * 2 + 1; //오른쪽 노드
//루트만 있는 상태 == 루트 pop
if(!this.heap[leftIdx]) return min;
// 오른쪽에 자식 노드가 없다면 즉, 왼쪽 노드에만 자식이 하나라면
if(!this.heap[rightIdx]) {
// 완전 이진트리이기 때문에 왼쪽부터 채워져 있어야함!
// 왼쪽과 비교해서 교체해주기
if(this.heap[leftIdx] < this.heap[curIdx]) {
this.swap(leftIdx, curIdx);
}
return min;
}
// 자식 노드가 오른쪽, 왼쪽 모두 있는 경우
// 현재 노드가 왼쪽 또는 오른쪽 큰 지 작은 지를 검사하며 반복
while(this.heap[leftIdx] < this.heap[curIdx] || this.heap[rightIdx] < this.heap[curIdx]) {
// 왼쪽과 오른쪽 자식 중에 더 작은 값과 현재 노드 교체
const minIdx = this.heap[leftIdx] > this.heap[rightIdx] ? rightIdx : leftIdx;
this.swap(minIdx, curIdx);
curIdx = minIdx;
leftIdx = curIdx * 2;
rightIdx = curIdx * 2 + 1;
}
return min;
}