定義
堆是一棵完全二叉樹。分為大頂堆和小頂堆
大頂推:所有節點都大於等於它的兩個子節點
小頂堆:所有節點都小於等於它的兩個子節點
虛擬碼
推排序步驟,以升序排列為例,用大頂堆。(降序排列,用小頂堆)
- 構建大頂推
- 把堆頂元素和堆尾元素交換,此時堆尾元素是最大的,堆的大小減一
- 堆頂元素下沉到指定位置
- 重複2-3步,直到堆的大小為1
關鍵在於如何構建一個大頂堆(對節點進行下沉):
從最後一個非葉子節點開始;
逐個檢查每個節點,如果一個節點的值小於其子節點的值,我們就將它與較大的子節點交換位置;
交換後,這個交換後的節點可能導致不滿足堆的特性,因此還需要繼續下沉(Heapify)
舉個例子
需要注意的是:對於一個完全二叉樹
它的最後一個非葉節點下標是Math.floor(len/2) - 1
,len是二叉樹的節點個數
下標為i的節點,它的左子節點是2*i+1
, 右子節點是2*i+2
比如說對於[5,2,7,3,6,1,4]
最後一個非葉子節點下標是Math.floor(len/2) - 1=2, 也就是說最後一個非葉子節點是7, 構建最大堆的過程如下:
時間複雜度
O(nlogn)
實現
function heapSort(arr) {
// 構建最大堆
let n = arr.length;
for(let i = Math.floor(n/2 - 1); i>=0;i--) {
heapify(arr, n, i) // 把下標為i的節點放到合適的位置上
}
while(n>1) {
// 交換堆頂元素和堆尾元素
[arr[0], arr[n-1]] = [arr[n-1], arr[0]]
// 堆的個數減一
n--
// 把堆頂元素放到合適的位置
heapify(arr, n, 0)
}
}
function heapify(arr, n, i) {
let largestIndex = i
let leftIndex = 2 * i + 1
let rightIndex = 2 * i + 2
if(leftIndex < n && arr[leftIndex] > arr[largestIndex]) {
largestIndex = leftIndex
}
if(rightIndex < n && arr[rightIndex] > arr[largestIndex]) {
largestIndex = rightIndex
}
if(largestIndex!==i) {
[arr[i], arr[largestIndex]] = [arr[largestIndex], arr[i]]
heapify(arr, n, largestIndex)
}
}