pop 메서드 (push보다 복잡한 로직이 필요함)
> const pop = () => {
[heap[1],heap[heap.length-1]] = [heap[heap.length-1],heap[1]]
heap.pop();
let idx = 1;
let left = undefined;
let right = undefined;
let std = undefined;
let height = Math.floor(Math.log2(heap.length-1));
let cnt = 0;
while (true) {
cnt += 1
std = heap[idx];
idx *= 2;
left = heap[idx];
right = heap[idx+1];
if (left && right) {
if (left < right && std > left) {
[heap[idx/2],heap[idx]] = [heap[idx],heap[idx/2]];
} else if (right<=left && std>right) {
[heap[idx/2],heap[idx+1]] = [heap[idx+1],heap[idx/2]];
} else {
break;
}
} else if (left && !right) {
if (std>left) {
[heap[idx/2],heap[idx]] = [heap[idx],heap[idx/2]];
} else {
break;
}
} else {
break;
}
idx += 1;
if (cnt === height) {
break;
}
}
}
매우 간단한 top메서드
위에서 구현을 할 때 특별히 알아둘 부분은 heap을 만들 때 배열을 이용했지만, 그 배열의 인덱스의 0은 비워놓는다는 점이다. 이는 힙의 특성상 노드의 개수가 2의 배수 단위로 증가할 때마다 높이가 한칸씩 +되는 것을 이용해 pop이나 push를 할 때 직계 부모요소 혹은 직계 자식 요소의 인덱스를 쉽게 구하려고 나누기 2를 하거나(부모요소), 곱하기 2를 해주는(자식 요소) 부분과 관련이 있다.
무슨 말인가 하면, 예를 들어, [2,3,5,4,6,7]
2 3 5 4 6 7
위와 같은 힙이 있다고 했을 때,
배열로 표현한 힙의 인덱스 0을 0으로 그냥 비워두면,
[0,2,3,5,4,6,7]이 되는데,
그러면 예를 들어, 인덱스 7에 새로운 요소를 예를 들어, 8을 넣는다고 했을 때, 8이 그 부모요소보다 작은지 큰지를 비교하기 위해
코드상에서 부모요소의 인덱스를 구할 수 있어야한다(할 때마다 탐색을 할 수는 없다..). 그 방법으로 새로 넣는 요소의 인덱스(이 경우에는 7)에 2를 나눠주면 7//2 = 3 => 인덱스 3의 값은 5로 맨 아래의 7의 오른쪽 노드로 8이 들어간다 했을 때 정확히 그의 부모요소는 5가 되는 것을 알 수 있다. 그러나, 인덱스 0을 0으로 비워놓지 않으면 이 계산이 통하지 않음을 알 수 있다. 역으로 pop을 할 때는 자식요소와 비교를 하는 것이 필요한데, 그 때는 부모요소의 인덱스에 2를 곱해주거나(왼쪽 자식 노드의 인덱스), 2를 곱한 다음에 1을 더해주면(오른쪽 자식 노드의 인덱스) 원하는 값을 얻을 수 있다.
실제로.. 이전에 배열로 만든 우선순위 큐와 for문으로 100000번의 push, pop을 시행하여(똑같은 조건으로) 비교해보니 실제 걸리는 시간 자체도 확연히 차이가 난다(시간복잡도 O(N)과 O(logN)의 차이는 어마무시하다는 것..) .