1. 程式人生 > >堆排序的JavaScript實現

堆排序的JavaScript實現

== @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實現