堆排序的JavaScript實現
阿新 • • 發佈:2018-06-24
== @param java -- 復雜 滿二叉樹 ges emp 註意
思想
把數組當做二叉樹來排序:
- 索引0是樹的根節點;
- 除根節點外,索引為N的節點的父節點索引是(N-1)/2;
- 索引為N的節點的左子節點索引是 2*N+1;
- 索引為N的節點的右子節點索引是 2*N+2;
代碼
function heapSort(arr) {
let heapSize = arr.length;
buildHeap(arr);//構造一個所有節點都滿足arr[parent[i]] > arr[i]的堆結構數組,這樣就把值最大的那個節點換到了根節點
while(heapSize > 1) { //*1
//在當前樹中,交換位於根節點的最大值和最後一個節點的值,這樣就把最大值排在了最後一個節點,這樣就排好了最大值
const temp = arr[0];
arr[0]=arr[heapSize-1];
arr[heapSize-1] = temp;
heapSize--;//當前樹中最後一個節點已經排好了值,故後面就不用再考慮這個節點,故新的樹的大小減一
if (heapSize>1) {
heapify(arr, heapSize, 0);//上面的交換操作產生了新的根節點,新的根節點只是通過跟最後一個節點交換得到的值,故新的根節點不滿足條件arr[parent[i]]<arr[i],所以要對根節點再次進行h
}
}
}
/**
* @description 構造一個所有節點都滿足arr[parent[i]] > arr[i]的堆結構數組
* @param {Array} arr 待排序數組
*/
function buildHeap(arr) {
const heapSize = arr.length;
const firstHeapifyIndex = Math.floor(heapSize/2-1);//從樹的倒數第二層的最後一個有子節點的節點(對於滿二叉樹就是倒數第二層的最後一個節點)開始進行heapify處理。(heapSize/2-1)就是這個最後一個有子節點的節點索引。
for (let i=firstHeapifyIndex; i >= 0; i--) {//從0到firstHeapifyIndex都要進行heapify處理,才能把最大的那個節點換到根節點
heapify(arr, heapSize, i);
}
}
/**
* @description 以數組arr的前heapSize個節點為樹,對其中索引為i的節點向子節點進行替換,直到滿足從i往下的子節點都有arr[parent[i]]>=arr[i]
* @param {*} arr TYPE Array 待排序的數組
* @param {*} heapSize TYPE Number 待排序的數組中要作為當前樹處理的從前往後數的節點個數,即待排序數組中前heapSize個點是要作為樹來處理
* @param {*} i TYPE Number arr數組中、heapSize長度的樹中的當前要進行往子節點替換的節點的索引
*/
function heapify(arr, heapSize, i) {
const leftIndex = i * 2 + 1;//索引i的節點的左子節點索引
const rightIndex = i * 2 + 2;//索引i的節點的右子節點索引
let biggestValueIndex = i;
if (leftIndex < heapSize && arr[leftIndex] > arr[biggestValueIndex]) {
//節點的最大index為heapSize-1
//註意:這兩次比較要跟arr[biggestValueIndex]比較,不能跟arr[i]比較,因為biggestValueIndex是會在左右i之間更新的
biggestValueIndex = leftIndex; //如果左子節點的值大於biggestValueIndex的值(此時就是根節點的值),那麽更新biggestValueIndex為左子節點索引
}
if (rightIndex < heapSize && arr[rightIndex] > arr[biggestValueIndex]) {
biggestValueIndex = rightIndex;//如果右子節點的值大於biggestValueIndex的值(此時可能是根節點的值,也可能是左子節點的值),那麽更新biggestValueIndex為右子節點索引
}
if (biggestValueIndex !== i) { //如果biggestValueIndex是左子節點索引或右子節點索引,那麽交換根節點與biggestValueIndex節點的值
const temp = arr[i];
arr[i] = arr[biggestValueIndex];
arr[biggestValueIndex] = temp;
//交換後,被交換的那個子節點(左子節點或右子節點)往下可能就不再滿足[parent[i]]>=arr[i],所以要繼續對biggestValueIndex進行heaify處理,即將biggestValueIndex可能需要和子節點進行值交換,直到樹的這個分支到葉子節點都滿足arr[parent[i]]>=arr[i]
heapify(arr, heapSize, biggestValueIndex);//要
}
}
工作過程
待畫圖
性能分析
- 時間復雜度:最好、平均、最壞都是 O(nlogn)
空間復雜度: O(1),不穩定
延伸:如果數組數量大於10,只找出最大的10個值呢?
上述代碼的*1行換成:
while(heapSize > arr.length - 10)
參考資料
-《學習JavaScript數據結構和算法》10.1.6
-《數據結構(C語言版)》9.4.2
堆排序的JavaScript實現