Code:
/**
* 最大堆
* @class
*/
class MaxHeap {
/**
* @type {number[]}
*/
#maxHeap;
/**
* 建堆
* @constructor
*/
constructor(nums) {
this.#maxHeap = nums === undefined ? [] : [...nums];
// 從底部葉節點的父元素位置開始從頂至底規範
for (let i = this.#parent(this.size() - 1); i >= 0; i--) {
this.#shiftDown(i);
}
}
/**
* 獲取左子節點的索引
* @param {number} i
*/
#left(i) {
return 2 * i + 1;
}
/**
* 獲取右子節點的索引
* @param {number} i
*/
#right(i) {
return 2 * i + 2;
}
/**
* 獲取節點父元素的索引
* @param {number} i
*/
#parent(i) {
return Math.floor((i - 1) / 2);
}
/**
* 獲取堆元素的數量
* @returns
*/
size() {
return this.#maxHeap.length;
}
/**
* 是否爲空堆
* @returns
*/
isEmpty() {
return this.#maxHeap.length === 0;
}
/**
* 獲取堆頂元素
* @returns
*/
peek() {
return this.#maxHeap[0];
}
/**
* 獲取堆的副本元素
* @returns
*/
getHeap() {
return [...this.#maxHeap];
}
/**
* 交換兩個元素的位置
* @param {number} i
* @param {number} j
*/
#swap(i, j) {
const tmp = this.#maxHeap[i];
this.#maxHeap[i] = this.#maxHeap[j];
this.#maxHeap[j] = tmp;
}
/**
* 從位置 i 開始,從底至頂堆化
* @param {number} i
*/
#shiftUp(i) {
while (true) {
// 父節點索引
const p = this.#parent(i);
// 節點所在位置合理時,跳出
if (p < 0 || this.#maxHeap[i] <= this.#maxHeap[p]) {
break;
}
this.#swap(i, p);
i = p;
}
}
/**
* 從位置 i 開始,從頂至底堆化
* @param {number} i
*/
#shiftDown(i) {
while (true) {
// 左子節點索引
const l = this.#left(i);
// 右子節點索引
const r = this.#right(i);
// 自身與左子、右子中的最大值
let max = i;
//// l索引在合理範圍內 且 l位置的元素大於max(i)位置的元素
if (l < this.size() && this.#maxHeap[l] > this.#maxHeap[max]) {
max = l;
}
//// r索引在合理範圍內 且 r位置的元素大於max位置的元素
if (r < this.size() && this.#maxHeap[r] > this.#maxHeap[max]) {
max = r;
}
// max的值依然是i
if (max === i) {
break;
}
// 交互不符合規則的兩元素
this.#swap(i, max);
// 更新i,並繼續循環
i = max;
}
}
/**
* 添加元素
* @param {number} val
*/
push(val) {
this.#maxHeap.push(val);
// 加入新元素後,由底至上重新規劃各元素位置
this.#shiftUp(this.size() - 1);
}
/**
* 移除堆頂元素
* @returns
*/
pop() {
if (this.isEmpty()) {
throw new Error('堆內已無元素!');
}
// 交換頭尾元素
this.#swap(this.size() - 1, 0);
// 彈出尾元素(即之前的頭元素,堆中的最大值)
const max = this.#maxHeap.pop();
// 由頂至下重新規劃各元素位置
this.#shiftDown(0);
return max;
}
}