js堆排序

ming1025發表於2020-10-23

  堆的預備知識

  • 堆是一個完全二叉樹。
  • 完全二叉樹: 二叉樹除開最後一層,其他層結點數都達到最大,最後一層的所有結點都集中在左邊(左邊結點排列滿的情況下,右邊才能缺失結點)。
  • 大頂堆:根結點為最大值,每個結點的值大於或等於其孩子結點的值。
  • 小頂堆:根結點為最小值,每個結點的值小於或等於其孩子結點的值。
  • 堆的儲存: 堆由陣列來實現,相當於對二叉樹做層序遍歷。

堆排序演算法

 

現在需要對如上二叉樹做升序排序,總共分為三步:

  1. 將初始二叉樹轉化為大頂堆(heapify)(實質是從第一個非葉子結點開始,從下至上,從右至左,對每一個非葉子結點做shiftDown操作),此時根結點為最大值,將其與最後一個結點交換。
  2. 除開最後一個結點,將其餘節點組成的新堆轉化為大頂堆(實質上是對根節點做shiftDown操作),此時根結點為次最大值,將其與最後一個結點交換。
  3. 重複步驟2,直到堆中元素個數為1(或其對應陣列的長度為1),排序完成。

程式碼實現

 1 let array = randomArray(1,100);
 2 console.log(array);
 3 let sortArray = heapSort(array);
 4 console.log(sortArray);
 5 //輸入起始值和終點值,隨機陣列
 6 function randomArray(start,end){
 7     var a=[],o={},random,step=end-start;
 8     while(a.length<step){
 9         random=start+parseInt(Math.random()*step);
10     if(!o["x"+random]){
11         a.push(random);
12         o["x"+random]=1;
13     };
14     };
15     return a;
16 };
17 //交換值            
18 function swap(A, i, j) {
19     let temp = A[i];
20     A[i] = A[j];
21     A[j] = temp; 
22 }
23             
24 // 將 i 結點以下的堆整理為大頂堆,注意這一步實現的基礎實際上是:
25 // 假設 結點 i 以下的子堆已經是一個大頂堆,shiftDown函式實現的
26 // 功能是實際上是:找到 結點 i 在包括結點 i 的堆中的正確位置。後面
27 // 將寫一個 for 迴圈,從第一個非葉子結點開始,對每一個非葉子結點
28 // 都執行 shiftDown操作,所以就滿足了結點 i 以下的子堆已經是一大
29 //頂堆
30 function shiftDown(A, i, length) {
31      let temp = A[i]; // 當前父節點
32     // j<length 的目的是對結點 i 以下的結點全部做順序調整
33     for(let j = 2*i+1; j<length; j = 2*j+1) {
34          temp = A[i];  // 將 A[i] 取出,整個過程相當於找到 A[i] 應處於的位置
35     if(j+1 < length && A[j] < A[j+1]) { 
36         j++;   // 找到兩個孩子中較大的一個,再與父節點比較
37     }
38     if(temp < A[j]) {
39         swap(A, i, j) // 如果父節點小於子節點:交換;否則跳出
40          i = j;  // 交換後,temp 的下標變為 j
41     } else {
42          break;
43     }
44     }
45 }
46 // 堆排序
47 function heapSort(A) {
48     // 初始化大頂堆,從第一個非葉子結點開始
49     for(let i = Math.floor(A.length/2-1); i>=0; i--) {
50          shiftDown(A, i, A.length);
51     }
52     // 排序,每一次for迴圈找出一個當前最大值,陣列長度減一
53     for(let i = Math.floor(A.length-1); i>0; i--) {
54         swap(A, 0, i); // 根節點與最後一個節點交換
55         shiftDown(A, 0, i); // 從根節點開始調整,並且最後一個結點已經為當
56     // 前最大值,不需要再參與比較,所以第三個引數
57      // 為 i,即比較到最後一個結點前一個即可
58     }
59     return A;
60 }            

 

 

 

 

 

 

相關文章