9. Heap

힙은 '우선 순위 큐'의 개념을 구현하기 위한 가장 적합한 방법이다. 이진 트리 형태를 가지며 우선순위가 높은 요소가 먼저 나가기 위해 요소가 삽입, 삭제될 때 바로 정렬되는 특징이 있다.

;

우선 순위 큐

여기서 '우선 순위 큐'는 자료 구조가 아니며, 개념임을 유의해야 한다.

일반적인 큐(Queue)는 FIFO(First input First output) 구조인 반면, 우선 순위 큐는 우선 순위가 높은 요소가 먼저 나가는 구조이다.

힙의 특징

  • 우선 순위가 높은 요소가 먼저 나가는 특징을 가진다.

  • 루트가 가장 큰 값이 되는 최대 힙(Max Heap), 루트가 가장 작은 값이 되는 최소 힙(Min Heap) 이 있다.

자바스크립트에서는 직접 구현해서 사용해야 한다.

힙 구현 (MaxHeap 기준)

class Heap {
  constructor() {
    this.heap = [null];
  }

  swap(a, b) {
    [this.heap[a], this.heap[b]] = [this.heap[b], this.heap[a]];
  }

  size() {
    return this.heap.length - 1;
  }
}

힙 요소 추가 알고리즘

  • 요소가 추가될 때는 가장 마지막 정점에 위치시킨다.

  • 이후, 부모 정점보다 우선순위가 높다면 부모 정점과 순서를 바꾼다.

  • 더이상 순서가 바뀌지 않을때까지 반복하여 적절한 위치를 찾는다.

  • 완전 이진 트리의 높이는 logN이기에 힙의 요소 추가 알고리즘은 O(logN) 시간복잡도를 가진다.

push(value) {
  this.heap.push(value);
  let currentIndex = this.heap.length -1;
  let parentIndex = Math.floor(currentIndex / 2);

  while (
    this.heap[parentIndex] !== undefined &&
    this.heap[currentIndex] > this.heap[parentIndex]
  ) {
    this.swap(currentIndex, parentIndex);
    currentIndex = parentIndex;
    parentIndex = Math.floor(currentIndex / 2);
  }
}

힙 요소 제거 알고리즘

  • 요소 제거는 루트 정점에서만 가능하다.

  • 루트 정점을 제거한 후 가장 마지막 정점을 루트에 위치시킨다.

  • 루트 정점의 두 자식 정점 중 우선순위가 높은 정점과 바꾼다.

  • 두 자식 정점이 우선 순위가 더 낮을 때까지 반복한다.

  • 완전 이진 트리의 높이는 logN이기에 힙 요소 제거 알고리즘은 O(logN) 시간 복잡도를 가진다.

pop () {
  const returnValue = this.heap[1];
  this.heap[1] = this.heap.pop();

  let currentIndex = 1;
  let leftChildIndex = 2;
  let rightChildIndex = 3;

  while (
    this.heap[currentIndex] > this.heap[leftChildIndex] ||
    this.heap[currentIndex] > this.heap[rightChildIndex]
  ) {
    if (this.heap[rightChildIndex] > this.heap[leftChildIndex]) {
      this.swap(currentIndex, rightChildIndex);
      currentIndex = rightChildIndex;
    } else {
      this.swap(currentIndex, leftChildIndex);
      currentIndex = leftChildIndex;
    }

    leftChildIndex = currentIndex * 2;
    rightChildIndex = currentIndex * 2 + 1;
  }
  return returnValue;
}

힙 구현 (MinHeap 기준)

힙 요소 추가 알고리즘

push (value) {
  this.heap.push(value);
  let currentIndex = this.heap.length - 1;
  let parentIndex = Math.floor(currentIndex / 2);

  while (parentIndex !== 0 &&
    this.heap[currentIndex] < this.heap[parentIndex]
  ) {
    this.swap(currentIndex, parentIndex);
    currentIndex = parentIndex;
    parentIndex = Math.floor(currentIndex / 2);
  }
}

힙 요소 삭제 알고리즘

pop () {
  const returnValue = this.heap[1];
  this.heap[1] = this.heap.pop();

  let currentIndex = 1;
  let leftChildIndex = 2;
  let rightChildIndex = 3;

  while (
    this.heap[leftChildIndex] !== undefined &&
    (this.heap[leftChildIndex] < this.heap[currentIndex] ||
      this.heap[rightChildIndex] < this.heap[currentIndex])
  ) {
    let smallerIndex = leftChildIndex;

    if (
      this.heap[rightChildIndex] !== undefined &&
      this.heap[rightChildIndex] < this.heap[smallerIndex]
    ) {
      smallerIndex = rightChildIndex;
    }

    this.swap(currentIndex, smallerIndex);
    currentIndex = smallerIndex;
    leftChildIndex = currentIndex * 2;
    rightChildIndex = (currentIndex *2) + 1;
  }
  return returnValue;
}

부가적인 유틸

swap (a, b) {
  [this.heap[a], this.heap[b]] = [this.heap[b], this.heap[a]];
}

size () {
  return this.heap.length -1;
}

Last updated