堆
(1)什麼是二叉堆
二叉堆本質上是一種完全二叉樹,二叉堆的經典表示方法是使用一個數組表示,其中:
- 根結點爲數組的第一個元素A[0]。
- 其它結點中,第i個結點和數組索引元素對應的關係爲:
A[(i – 1) / 2] | 返回第i個結點的父結點 |
---|---|
A[(2 * i) + 1] | 返回第i個結點的左兒子結點 |
A[(2 * i) + 2] | 返回第i個結點的右兒子結點 |
它分爲兩個類型:
-
最大堆
最大堆任何一個父節點的值,都大於等於它左右孩子節點的值。
-
最小堆
最小堆任何一個父節點的值,都小於等於它左右孩子節點的值。
假設父節點的下標是parent,那麼它的左孩子下標就是 2*parent+1;它的右孩子下標就是 2*parent+2 。
(2)二叉堆的主要操作
-
堆的構建
大頂堆
//從第一個非葉子節點開始依次對數組中的元素進行下沉操作 //和孩子節點的最大值max比較 //大於max — 不需要在下沉 //小於max — 和max交換位置 - 繼續和下一層孩子節點比較,直到隊列末尾 function ajustMaxHeap(array, index, length) { for (let i = 2 * index + 1; i < length; i = 2 * i + 1) { if (i + 1 < length && array[i + 1] > array[i]) { i++; } if (array[index] >= [array[i]]) { break; } else { [array[index], array[i]] = [array[i], array[index]]; index = i; } } } function createMaxHeap(arr, length) { for (let i = Math.floor(length / 2) - 1; i >= 0; i--) { ajustMaxHeap(arr, i, length); } return arr; }
小頂堆
//從第一個非葉子節點開始依次對數組中的元素進行下沉操作 //和孩子節點的最小值min比較 //小於min — 不需要在下沉 //大於min — 和min交換位置(下沉) - 繼續和下一層孩子節點比較,直到隊列末尾 function ajustMinHeap(array, index, length) { for (let i = 2 * index + 1; i < length; i = 2 * i + 1) { if (i + 1 < length && array[i + 1] < array[i]) { i++; } if (array[index] < [array[i]]) { break; } else { [array[index], array[i]] = [array[i], array[index]]; index = i; } } } function createMinHeap(arr, length) { for (let i = Math.floor(length / 2) - 1; i >= 0; i--) { ajustMinHeap(arr, i, length); } return arr; }
-
堆的插入
//由於堆屬於優先隊列,只能從末尾添加 //添加後有可能破壞堆的結構,需要從下到上進行調整 //如果元素小於父元素,上浮 //以小頂堆爲例: function minHeapAdd(array = [], element) { array.push(element); if (array.length > 1) { let index = array.length - 1; let target = Math.floor((index - 1) / 2); while (target >= 0) { array[target]); if (array[index] < array[target]) { [array[index], array[target]] = [array[target], array[index]] index = target; target = Math.floor((index - 1) / 2); } else { break; } } } return array; }
-
堆的移除
//由於堆屬於優先隊列,只能從頭部移除 //移除頭部後,使用末尾元素填充頭部,開始頭部下沉操作 //以小頂堆爲例: function minHeapPop(array = []) { let result = null; if (array.length > 1) { result = array[0]; array[0] = array.pop(); ajustMinHeap(array, 0, array.length); } else if (array.length === 1) { return array.pop(); } return result; }
-
封裝
class Heap { constructor(arr, type) { this.data = [...arr]; this.type = type; this.size = this.data.length; } create() { for (let i = Math.floor((this.size / 2) - 1); i >= 0; i--) { this.ajust(i); } } ajust (index) { const arr = this.data; for (let i = 2 * index + 1; i < this.size; i = 2 * i + 1) { if (i + 1 < this.size) { if ((this.type === 'max' && arr[i + 1] > arr[i]) || (this.type === 'min' && arr[i + 1] < arr[i])) { i++; } } if ((this.type === 'max' && arr[index] < [arr[i]]) || (this.type === 'min' && arr[index] > [arr[i]])) { [arr[index], arr[i]] = [arr[i], arr[index]]; index = i; } else { break; } } } add (element) { const arr = this.data; arr.push(element); if (this.size > 1) { let index = this.size - 1; let target = Math.floor((index - 1) / 2); while (target >= 0) { if ((this.type === 'min' && arr[index] < arr[target]) || (this.type === 'max' && arr[index] > arr[target])) { [arr[index], arr[target]] = [arr[target], arr[index]] index = target; target = Math.floor((index - 1) / 2); } else { break; } } } } pop () { const arr = this.data; let result = null; if (this.size > 1) { result = arr[0]; arr[0] = arr.pop(); this.ajust(0, this.size); } else if (this.size === 1) { return arr.pop(); } return result; } } var heap = new Heap('max'); heap.add(6) heap.add(10) console.log(heap.value); console.log(heap.pop()); console.log(heap.value);