현재 노드 번호 i
- 현재 노드 parent node의 번호 =
(i - 1) / 2- 현재 노드의 left child node의 번호 =
i * 2 + 1- 현재 노드의 right child node의 번호 =
i * 2 + 2
-> 1차원 배열로 이진트리를 나타내기 용이하다.
Max heap, Min heap 존재Key (parent) >= Key (child)Key (parent) <= Key (child)
최대힙, 최소힙은 값의 비교 연산자만 다르고 나머지는 똑같기 때문에 최소힙은 생략함
주어진 배열 = [10, 15, 30, 40, 50, 100, 40]
현재노드 Index = 6
부모노드 Index: Math.floor((자식노드 index - 1) / 2) = 2
왼쪽 자식 노드 Index = (부모노드 2) + 1 = 5
오른쪽 자식 노드 Index = (부모노드 2) + 2 = 6
// 부모노드 인덱스
getParentIndex(i) {
return Math.floor((i - 1) / 2);
}
// 왼쪽 자식노드 인덱스
getLeftChildIndex(i) {
return i * 2 + 1;
}
// 오른쪽 자식노드 인덱스
getRightChildIndex(i) {
return i * 2 + 2;
}
push()swap()heapiftUp()maxValuethis.data[0] = this.data[this.data.length - 1];this.data.length--;heapifyDown(), swap()heapifyDown()poll(), return maxValueclass Heap {
constructor() {
this.data = [];
}
// 기본 셋팅
getParentIndex(i) {
return Math.floor(i - 1 / 2);
}
getLeftChildIndex(i) {
return i * 2 + 1;
}
getRightChildIndex(i) {
return i * 2 + 2;
}
swap(i1, i2) {
const temp = this.data[i1];
this.data[i1] = this.data[i2];
this.data[i2] = temp;
}
}
- 원소를 맨 마지막에 넣는다
push()- 부모의 노드와 비교해서 더 크다면 자리를 바꾼다
swap()- 현재 노드가 부모 노드보다 작거나 가장 위에 도달하지 않을 때까지 2의 과정을 반복한다
heapiftUp()
class Heap {
...
// push 배열 끝에 넣기
push(key) {
this.data[this.data.length] = key;
// 가장 큰 값일 수 있다. heap요구사항에 따라 자리를 바꿔줘야함 > hepifyUp()통해서 이뤄진다.
this.heapifyUp();
}
}
- currentIndex : 최근에 삽입된 노드의 Index
- while문 : currentIndex의 요소가 상위요소보다 클 때까지 돌린다. 현재 요소 ( 가장 마지막, 밑에있던 요소)와 부모 요소의 값을 비교한다. 현재 요소가 크면 위로 올려야 하기 때문에 swap()을 쓴다.
- 비교를 거친후 새 위치에 대해 이를 반복해야 하므로 currentIndex를 비교했던 부모 요소의 Index로 재할당시 킨다.
heapifyUp() {
let currentIndex = this.data.length - 1;
// current요소가 상위요소보다 클 때까지 돌린다.
// 현재 요소 (가장마지막, 밑에있던 요소) 와 부모 요소의 값을 비교 한다.
// 현재요소가 크면 위로 올려야하기 때문에 swap()을 쓴다.
while (
this.data[currentIndex] > this.data[this.getParentIndex(currentIndex)]
) {
this.swap(currentIndex, this.getParentIndex(currentIndex));
// currentIndex를 비교했던 부모요소로 재할당시킨다.
currentIndex = this.getParentIndex(currentIndex);
}
}
- currendIndex : 최상위 노드를 지정한다.
- while문 : 최상위에 있는 현재 노드보다 자식 노드가 더 크면 현재 노드와 자식 노드를 바꿔주는 while문을 반복한다.(이때 왼쪽 노드를 기준으로 비교 검사할 것이기 때문에 왼쪽 노드가 있을 때까지 라는 조건을 넣어준다.)
- biggestChildIndex : currentIndex = 0 일 때 부 자식의 왼쪽 노드의 Indexr값을 변수로 지정해준다.
- 만약 자식의 오른쪽 노드가 있고, 자식의 오른쪽 노드가 왼쪽 노드보다 크다면 3. 의 date [biggestChildIndex]를 자식의 오른쪽 노드로 지정해준다.
- 4번의 조건이 불충분하면 자식의 왼쪽 노드가 더 크다는 뜻이므로 현재 heap배열의 최상단 노드가 date [biggestChildIndex](자식 왼쪽 노드) 보다 큰지 비교해준다.
- date [biggestChildIndex]가 더 크다면 swap()을 통해 서로 간의 노드 위치를 바꿔준다.
- 이후 새로운 위치에서의 비교 연산을 위해 currentIndex를 biggestChildIndex로 바꿔준다.
이 함수가 끝나면 다시 poll()로 돌아가게 되고 최종적으로 maxValue를 순서대로 리턴하게 된다.
heapifyDown() {
// index 0 최상위 요소
let currentIndex = 0;
// 현재 요소를 맨위에 놓고 자식이 더 크면 현재와 자식을 바꿔주는 while문 반복
// 왼쪽 요소가 있는지 확인
while (this.data[this.getLeftChildIndex(currentIndex)] !== undefined) {
let biggestChildIndex = this.getLeftChildIndex(currentIndex);
// 오른쪽노드와, 왼쪽 노드중 더 큰값을 biggestChildIndex 로 설정
if (
this.data[this.getRightChildIndex(currentIndex)] !== undefined &&
this.data[this.getRightChildIndex(currentIndex)] >
this.data[this.getLeftChildIndex(currentIndex)]
) {
biggestChildIndex = this.getRightChildIndex(currentIndex);
}
// 자식노드가 더 크다면 부모노드랑, 자식노드랑 변경함
if (this.data[currentIndex] < this.data[biggestChildIndex]) {
this.swap(currentIndex, biggestChildIndex);
currentIndex = biggestChildIndex;
} else {
return;
}
}
}
// max heap 구현
class Heap {
constructor() {
this.data = [];
}
// 기본 셋팅
getParentIndex(i) {
return Math.floor(i - 1 / 2);
}
getLeftChildIndex(i) {
return i * 2 + 1;
}
getRightChildIndex(i) {
return i * 2 + 2;
}
swap(i1, i2) {
const temp = this.data[i1];
this.data[i1] = this.data[i2];
this.data[i2] = temp;
}
// push 배열 끝에 넣기
push(key) {
this.data[this.data.length] = key;
// 가장 큰 값일 수 있다. heap요구사항에 따라 자리를 바꿔줘야함 > hepifyUp()통해서 이뤄진다.
this.heapifyUp();
}
// 인자 필요없다 항상 배열의 마지막 요소를 다루기 때문
heapifyUp() {
let currentIndex = this.data.length - 1;
// current요소가 상위요소보다 클 때까지 돌린다.
// 현재 요소 (가장마지막, 밑에있던 요소) 와 부모 요소의 값을 비교 한다. 현재요소가 크면 위로 올려야하기 때문에 swap()을 쓴다.
while (
this.data[currentIndex] > this.data[this.getParentIndex(currentIndex)]
) {
this.swap(currentIndex, this.getParentIndex(currentIndex));
// currentIndex를 비교했던 부보요소로 재할당시킨다.
currentIndex = this.getParentIndex(currentIndex);
}
}
// Push 뿐만아니라 poll도 할 줄 알아야 함(추출)
poll() {
// 가장 최상단 요소가 최댓값일 테고
const maxValue = this.data[0];
// 그 최상단 요소와 가장 아래에있는 요소로 대체한다. (제거해도되는데 여기서는 대체함)
this.data[0] = this.data[this.data.length - 1];
// 배열의 길이를 줄여 맨위에 할당했던 마지막 요소를 없애준다.
this.data.length--;
// 여전히 heap의 규칙인지 확인해야한다.
// 이때 위에서부터 제일 아래로 실행되는 heapifyDown함수 실행
// 위에서 끝에있던 요소를 첫번째로 대체했었기 때문에
this.heapifyDown();
return maxValue;
}
heapifyDown() {
// index 0 최상위 요소
let currentIndex = 0;
// 현재 요소를 맨위에 놓고 자식이 더 크면 현재와 자식을 바꿔주는 while문 반복
// 왼쪽 요소가 있는지 확인
while (this.data[this.getLeftChildIndex(currentIndex)] !== undefined) {
let biggestChildIndex = this.getLeftChildIndex(currentIndex);
if (
this.data[this.getRightChildIndex(currentIndex)] !== undefined &&
this.data[this.getRightChildIndex(currentIndex)] >
this.data[this.getLeftChildIndex(currentIndex)]
) {
biggestChildIndex = this.getRightChildIndex(currentIndex);
}
if (this.data[currentIndex] < this.data[biggestChildIndex]) {
this.swap(currentIndex, biggestChildIndex);
currentIndex = biggestChildIndex;
} else {
return;
}
}
}
}
O(logN) 이고, 반복하는 최대 횟수도 O(logN) 이 된다.O(logN) 이고, 반복하는 최대 횟수도 O(logN) 이 된다.| Big-O (시간 복잡도) | 삽입 | 추출, 삭제 |
|---|---|---|
| 힙 (heap) | O(logN) | O(logN) |