[자료구조] 힙(Heap)
2022년 05월 09일
힙(Heap)
우선 순위 큐를 위해 만들어진 자료구조로, 이진 트리 형태를 가지며 요소가 삽입, 삭제 될 때 바로 정렬되는 특징이 있다.
참고 : https://dmchoi77.github.io/DataStructure&Algorithm/Tree/
그럼 우선 순위 큐와 힙의 차이가 뭐냐?
우선 순위 큐는 힙뿐만 아니라 배열이나 연결 리스트로도 구현이 가능하다. 대신 배열과 연결 리스트는 선형 자료 구조이기 때문에 삽입, 삭제 연산 시 시간 복잡도가 O(n)이다.
반면에, 힙은 완전 이진 트리 구조이기 때문에 시간 복잡도가 O(log n)이다.
언제 사용해야 좋을까?
매 루프를 돌면서 최대 값 또는 최소 값을 찾아야하는 경우에 흔히 세 가지 방법이 있다.
- -매 루프마다 Math.max/Math.min 함수 호출 -> 시간 복잡도 O(n)
- -매 루프마다 정렬 -> 시간 복잡도 O(nlog n)
- -heap 사용 -> 시간 복잡도 O(log n)
그래서 가장 큰 값이나 가장 작은 값을 찾을 때 heap을 사용하는 것이 효율적이다.
힙의 특징
-우선 순위가 높은 요소가 먼저 나가게 된다.
-루트 노드가 가장 큰 값이 되는 최대 힙과 루트 노드가 가장 작은 값이 되는 최소힙이 있다.
힙 요소 추가 알고리즘
-요소가 추가될 때는 트리의 가장 마지막 정점에 위치에 추가된다.
-추가 후, 부모 정점보다 우선순위가 높으면 부모 정점과 위치를 서로 바꾼다.
-계속 반복하면 결국 가장 우선순위가 높은 노드가 루트 노드에 위치하게 된다.
-완전 이진 트리의 높이는 log n이기에 힙의 요소 추가 알고리즘은 O(log n)의 시간 복잡도를 가지게 된다.
힙 요소 제거 알고리즘
-요소 제거는 루트 노드만 가능하다.
-루트 노드가 제거되면 가장 마지막 정점의 노드가 루트로 이동한다.
-루트 노드와 자식 노드의 우선 순위를 비교해 더 우선 순위가 높은 노드를 위로 올린다.
-부모 노드보다 두 자식 노드의 우선 순위가 더 낮을 때까지 반복
-완전 이진 트리의 높이는 log n이기에 힙의 요소 제거 알고리즘은 O(log n)의 시간 복잡도를 가지게 된다.
class MaxHeap {
constructor() {
this.heap = [null];
}
push(value) { // 새 요소를 마지막 위치에 추가하고 부모와 계속 비교하며 순서를 바꿈
this.heap.push(value);
let currentIndex = this.heap.length - 1;
let parentIndex = Math.floor(currentIndex / 2);
while (parentIndex !== 0 && this.heap[parentIndex] < value) { // 부모가 더 우선 순위가 낮거나 루트 노드가 아닐 때까지 계속 루프를 돌아~
const temp = this.heap[parentIndex];
this.heap[parentIndex] = value; // 부모와 자리를 바꿈
this.heap[currentIndex] = temp;
currentIndex = parentIndex;
parentIndex = Math.floor(currentIndex / 2); // 인덱스 재설정
}
}
pop() {
if (this.heap.length === 2) return this.heap.pop(); // 루트 노드만 남은 경우
const returnValue = this.heap[1]; // 루투 노드를 반환하기 위해 임시로 저장
this.heap[1] = this.heap.pop(); // 루트 노드를 가장 마지막 노드로 대체
let currentIndex = 1;
let leftIndex = 2;
let rightIndex = 3;
while (
this.heap[currentIndex] < this.heap[leftIndex] ||
this.heap[currentIndex] < this.heap[rightIndex]
) {
if (this.heap[leftIndex] < this.heap[rightIndex]) {
const temp = this.heap[currentIndex];
this.heap[currentIndex] = this.heap[rightIndex];
this.heap[rightIndex] = temp;
currentIndex = rightIndex;
} else {
const temp = this.heap[currentIndex];
this.heap[currentIndex] = this.heap[leftIndex];
this.heap[leftIndex] = temp;
currentIndex = leftIndex;
}
leftIndex = currentIndex * 2;
rightIndex = currentIndex * 2 + 1;
}ㄴ
return returnValue;
}
}
Tags