정수를 요소로 갖는 배열을 입력받아 이진 힙(binary heap)*을 리턴해야 합니다.
- 이진 힙(binary heap)은 노드의 값이 특정한 순서를 가지고 있는 완전 이진 트리(Complete Binary Tree)입니다.
- 완전 이진 트리는 이진 트리의 (마지막 레벨 또는 마지막 깊이를 제외하고) 모든 레벨이 노드로 가득 채워져 있어야 합니다. 마지막 레벨은 왼쪽부터 차례대로 채워져 있습니다.
- 이진 힙에서 부모 노드의 값이 (이진 트리이므로 2개의) 자식 노드의 값보다 큰 경우를 최대 힙(max heap), 반대의 경우를 최소 힙(min heap)이라고 합니다.
let output = binaryHeap([5, 4, 3, 2, 1]);
console.log(output); // --> [5, 4, 3, 2, 1]
output = binaryHeap([3, 1, 21]);
console.log(output); // --> [21, 1, 3]
output = binaryHeap([4, 10, 3, 5, 1]);
console.log(output); // --> [10, 5, 3, 4, 1]
// 아래 코드는 수정하지 마세요.
function swap(idx1, idx2, arr) {
// 두 변수를 바꾸는 방법
// 1) 임시 변수를 활용한 방법
// let temp = arr[idx1];
// arr[idx1] = arr[idx2];
// arr[idx2] = temp;
// 2) Destructuring assignment를 활용한 방법
// arr이 reference type이라 가능
[arr[idx1], arr[idx2]] = [arr[idx2], arr[idx1]];
// 3) XOR 연산을 활용한 방법
// arr이 reference type이라 가능
// arr[idx1] ^= arr[idx2];
// arr[idx2] ^= arr[idx1];
// arr[idx1] ^= arr[idx2];
}
function getParentIdx(idx) {
// 부모의 인덱스 번호 확인하기
// 최대 힙의 경우 왼쪽부터 채워진다.
// 0의 자식은 1, 2 / 1의 자식은 3, 4 / 2의 자식은 5, 6...
return Number.parseInt((idx - 1) / 2)
}
function insert(heap, item) {
heap.push(item);
childIdx = heap.length - 1;
parentIdx = getParentIdx(childIdx);
while (parentIdx >= 0 && heap[parentIdx] < heap[childIdx]) {
swap(parentIdx, childIdx, heap);
childIdx = parentIdx;
parentIdx = getParentIdx(childIdx);
}
return heap
}
// downHeapify 방식으로??
// 아래 코드는 수정하지 마세요.
const binaryHeap = function (arr) {
return arr.reduce((heap, item) => {
return insert(heap, item);
}, []);
};
정수를 요소로 갖는 배열을 입력받아 오름차순으로 정렬하여 리턴해야 합니다.
// 아래 코드는 수정하지 마세요.
function swap(idx1, idx2, arr) {
[arr[idx1], arr[idx2]] = [arr[idx2], arr[idx1]];
}
function getParentIdx(idx) {
return Number.parseInt((idx - 1) / 2)
}
function insert(heap, item) {
heap.push(item);
let childIdx = heap.length - 1;
let parentIdx = getParentIdx(childIdx);
// binaryHeap에서 부등호만 바꿔줬다.
while (parentIdx >= 0 && heap[parentIdx] > heap[childIdx]) {
swap(parentIdx, childIdx, heap);
childIdx = parentIdx;
parentIdx = getParentIdx(childIdx);
}
return heap
}
function removeRoot(heap) {
// 가장 마지막 노드와 root를 교환한 후, 배열 길이를 줄여준 다음 최소 힙 구현을 해준다.
swap(0, heap.length - 1, heap); // 가장 마지막 노드와 root 교환
heap.pop(); // 배열 길이 1만큼 축소
if (heap.length === 0) return [];
let curIdx; // root부터 아래로 내려가면서 확인할 예정
let parentIdx = 0; // root idx
while (curIdx !== parentIdx) {
curIdx = parentIdx; // root부터 시작!
let leftChild = curIdx * 2 + 1;
let rightChild = curIdx * 2 + 2;
if (leftChild < heap.length && heap[leftChild] < heap[parentIdx]) {
parentIdx = leftChild; // 자식보다 부모가 크면 최소 힙 구현을 위해 자식, 부모 인덱스에 자녀 인덱스 할당 후 swap 진행
}
if (rightChild < heap.length && heap[rightChild] < heap[parentIdx]) {
parentIdx = rightChild;
}
swap(curIdx, parentIdx, heap);
}
return heap
}
// 아래 코드는 수정하지 마세요.
const binaryHeap = function (arr) {
return arr.reduce((heap, item) => {
return insert(heap, item);
}, []);
};
const heapSort = function (arr) {
let minHeap = binaryHeap(arr);
const result = [];
// 큐처럼 활용하는구나
while (minHeap.length > 0) {
result.push(minHeap[0]); // 최소힙의 root 값을 넣어주고 다시 최소힙 구현
minHeap = removeRoot(minHeap);
}
return result;
};