가수면
이진 힙 구현 본문
이진 탐색 트리와 이진 힙 비교
- 힙은 각 노드의 값이 자식 노드보다 크거나 같음(Max Heap의 경우)
- 이진 탐색 트리는 왼쪽 자식 노드의 값이 가장 작고, 그 다음 부모 노드, 그 다음 오른쪽 자식 노드 값이 가장 큼
- 힙은 이진 탐색 트리의 조건인 자식 노드에서 작은 값은 왼쪽, 큰 값은 오른쪽이라는 조건은 없음
- 힙의 왼쪽 및 오른쪽 자식 노드의 값은 오른쪽이 클 수도 있고, 왼쪽이 클 수도 있음
최대 이진 힙
왼쪽 오른쪽 구분 없이 부모 노드가 자식 노드보다 항상 큰 트리 구조
왼쪽 자식 노드 인덱스 => 2n+1
오른쪽 자식 노드 인덱스 => 2n + 2
부모 인덱스 => Math.floor((n - 1) / 2)
insert 매소드
class MaxBinaryHeap {
constructor() {
this.values = [];
}
insert(element) {
this.values.push(element);
this.bubbleUp();
}
bubbleUp() {
// 맨 마지막부터 시작
let index = this.values.length - 1;
// insert된 요소
const element = this.values[index];
while (index > 0) {
// insert된 요소의 부모 요소 찾기
let parentindex = Math.floor((index - 1) / 2);
let parent = this.values[parentindex];
if (element <= parent) break;
// 부모 노드보다 자식 노드가 크면 스위칭
this.values[parentindex] = element;
this.values[index] = parent;
index = parentindex;
}
}
}
extractMax 매소드
class MaxBinaryHeap {
constructor() {
this.values = [];
}
extractMax() {
// shift하지 않고 max에 end를 덮어씌우는 방식
const max = this.values[0];
const end = this.values.pop();
if (this.values.length > 0) {
this.values[0] = end;
// end가 max로 왔으니 아래로 내리는 작업을 해줘야 함
this.sinkDown();
}
return max;
}
sinkDown() {
let index = 0;
// length-1할 경우 조건문을 수정하면 됨
const length = this.values.length;
const element = this.values[0];
while (true) {
let leftChildindex = 2 * index + 1;
let rightChildindex = 2 * index + 2;
let leftChild, rightChild;
let swap = null;
// length - 1이면 <=로 수정
if (leftChildindex < length) {
leftChild = this.values[leftChildindex];
if (leftChild > element) {
swap = leftChildindex;
}
}
if (rightChildindex < length) {
rightChild = this.values[rightChildindex];
if (
(swap === null && rightChild > element) ||
(swap !== null && rightChild > leftChild)
) {
swap = rightChildindex;
}
}
if (swap === null) return;
this.values[index] = this.values[swap];
this.values[swap] = element;
index = swap;
}
}
}
우선 순위 힙
우선 순위 큐를 구현할 때 보통 힙을 사용하는 것일 뿐이지 우선 순위 큐는 다른 방법으로도 구현할 수 있음
최대 이진 힙에서 부등호(높은 value가 위로 오는 것에서 낮은 우선 순위가 위로 오는 것으로 바꿈)를 변경하고, 비교할 때 value가 아닌 priority(실제 구현했을 때 우선 순위로 비교할 것)를 비교하는 것으로 바꾸기만 하면 됨.
class Node {
constructor(val, priority) {
this.val = val;
this.priority = priority;
}
}
class PriorityQueue {
constructor() {
this.values = [];
}
enqueue(val, priority) {
let newNode = new Node(val, priority);
this.values.push(newNode);
this.bubbleUp();
}
bubbleUp() {
let idx = this.values.length - 1;
const element = this.values[idx];
while (idx > 0) {
let parentIdx = Math.floor((idx - 1) / 2);
let parent = this.values[parentIdx];
if (element.priority >= parent.priority) return;
this.values[parentIdx] = element;
this.values[idx] = parent;
idx = parentIdx;
}
}
dequeue() {
const min = this.values[0];
const end = this.values.pop();
if (this.values.length > 0) {
this.values[0] = end;
this.sinkDown();
}
return min;
}
sinkDown() {
let idx = 0;
const length = this.values.length;
const element = this.values[0];
while (true) {
let leftChildIdx = 2 * idx + 1;
let rightChildIdx = 2 * idx + 2;
let leftChild, rightChild;
let swap = null;
if (leftChildIdx < length) {
leftChild = this.values[leftChildIdx];
if (leftChild.priority < element.priority) {
swap = leftChildIdx;
}
}
if (rightChildIdx < length) {
rightChild = this.values[rightChildIdx];
if (
(swap === null && rightChild.priority < element.priority) ||
(swap !== null && rightChild.priority < leftChild.priority)
) {
swap = rightChildIdx;
}
}
if (swap === null) break;
this.values[idx] = this.values[swap];
this.values[swap] = element;
idx = swap;
}
}
}
Comments