搞定大廠演算法面試之leetcode精講12.堆
阿新 • • 發佈:2021-11-30
大廠演算法面試之leetcode精講12.堆
視訊講解(高效學習):點選學習
目錄:
延伸:
滿二叉樹:除葉子節點外,所有的節點都有兩個子節點,這類二叉樹稱作滿二叉樹(Full Binarry Tree),如下圖:
完全二叉樹
堆是一個完全二叉樹,所以我們可以採用陣列實現,不會浪費太多空間,堆中的每個節點的值總是不大於或不小於其父節點的值,堆分為大頂堆和小頂堆,大頂堆堆頂是元素中最大的一個,小頂堆堆頂是最小的,在向堆中加入元素的時候,能動態調整堆內元素的順序,始終保持堆的性質。
堆的特點:
- 內部資料是有序的
- 可以彈出堆頂的元素,大頂堆就是彈出最大值,小頂堆就是彈出最小值
- 每次加入新元素或者彈出堆頂元素後,調整堆使之重新有序僅需要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; } }