1. 程式人生 > 其它 >搞定大廠演算法面試之leetcode精講12.堆

搞定大廠演算法面試之leetcode精講12.堆

大廠演算法面試之leetcode精講12.堆

視訊講解(高效學習):點選學習

目錄:

1.開篇介紹

2.時間空間複雜度

3.動態規劃

4.貪心

5.二分查詢

6.深度優先&廣度優先

7.雙指標

8.滑動視窗

9.位運算

10.遞迴&分治

11剪枝&回溯

12.堆

13.單調棧

14.排序演算法

15.連結串列

16.set&map

17.棧

18.佇列

19.陣列

20.字串

21.樹

22.字典樹

23.並查集

24.其他型別題

延伸:

滿二叉樹:除葉子節點外,所有的節點都有兩個子節點,這類二叉樹稱作滿二叉樹(Full Binarry Tree),如下圖:

完全二叉樹

:若設二叉樹的高度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層從右向左連續缺若干結點,這就是完全二叉樹。

堆是一個完全二叉樹,所以我們可以採用陣列實現,不會浪費太多空間,堆中的每個節點的值總是不大於或不小於其父節點的值,堆分為大頂堆和小頂堆,大頂堆堆頂是元素中最大的一個,小頂堆堆頂是最小的,在向堆中加入元素的時候,能動態調整堆內元素的順序,始終保持堆的性質。

堆的特點:
  • 內部資料是有序的
  • 可以彈出堆頂的元素,大頂堆就是彈出最大值,小頂堆就是彈出最小值
  • 每次加入新元素或者彈出堆頂元素後,調整堆使之重新有序僅需要O(logn)的時間
堆的實現
  • 用陣列實現,堆從上到下,從左到右一一對應陣列中的元素
  • 節點父節點索引 parentIndex = [(index - 1) / 2],左節點索引leftIndex = index * 2 + 1,右節點索引 rightIndex = index * 2 + 2
  • 第一個非葉子節點是[size / 2]
向堆中新增元素
  • 把新資料新增到樹的最後一個元素,也就是陣列的末尾
  • 把末尾節點向上調整,即bubbleUp
  • 時間複雜度O(logn)

動畫過大,點選檢視

彈出堆中的元素
  • 交換根節點與最後一個節點的值
  • 刪除最後一個節點
  • 把根節點向下調整
  • 時間複雜度O(logn)

動畫過大,點選檢視

從一個數組中取出最小值的複雜度:
完整程式碼
class Heap {
    constructor(comparator = (a, b) => a - b, data = []) {
        this.data = data;
        this.comparator = comparator;//比較器
        this.heapify();//堆化
    }

    heapify() {
        if (this.size() < 2) return;
        for (let i = Math.floor(this.size()/2)-1; i >= 0; i--) {
            this.bubbleDown(i);//bubbleDown操作
        }
    }

    peek() {
        if (this.size() === 0) return null;
        return this.data[0];//檢視堆頂
    }

    offer(value) {
        this.data.push(value);//加入陣列
        this.bubbleUp(this.size() - 1);//調整加入的元素在小頂堆中的位置
    }

    poll() {
        if (this.size() === 0) {
            return null;
        }
        const result = this.data[0];
        const last = this.data.pop();
        if (this.size() !== 0) {
            this.data[0] = last;//交換第一個元素和最後一個元素
            this.bubbleDown(0);//bubbleDown操作
        }
        return result;
    }

    bubbleUp(index) {
        while (index > 0) {
            const parentIndex = (index - 1) >> 1;//父節點的位置
            //如果當前元素比父節點的元素小,就交換當前節點和父節點的位置
            if (this.comparator(this.data[index], this.data[parentIndex]) < 0) {
                this.swap(index, parentIndex);//交換自己和父節點的位置
                index = parentIndex;//不斷向上取父節點進行比較
            } else {
                break;//如果當前元素比父節點的元素大,不需要處理
            }
        }
    }

    bubbleDown(index) {
        const lastIndex = this.size() - 1;//最後一個節點的位置
        while (true) {
            const leftIndex = index * 2 + 1;//左節點的位置
            const rightIndex = index * 2 + 2;//右節點的位置
            let findIndex = index;//bubbleDown節點的位置
            //找出左右節點中value小的節點
            if (
                leftIndex <= lastIndex &&
                this.comparator(this.data[leftIndex], this.data[findIndex]) < 0
            ) {
                findIndex = leftIndex;
            }
            if (
                rightIndex <= lastIndex &&
                this.comparator(this.data[rightIndex], this.data[findIndex]) < 0
            ) {
                findIndex = rightIndex;
            }
            if (index !== findIndex) {
                this.swap(index, findIndex);//交換當前元素和左右節點中value小的
                index = findIndex;
            } else {
                break;
            }
        }
    }

    swap(index1, index2) {//交換堆中兩個元素的位置
        [this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]];
    }

    size() {
        return this.data.length;
    }
}