JS Heap(最大堆)

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;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章