徹底二叉樹適合採用順序存儲的方式,所以一個數組能夠當作一個徹底二叉樹。java
從一個結點的編號就可推得其雙親,左、右孩子,兄弟等結點的編號。假設編號爲i的結點是ki(1≤i≤n),則有:算法
①若i>1,則ki的雙親編號爲i/2;若i=1,則Ki是根結點,無雙親。數組
②若2i≤n,則Ki的左孩子的編號是2i;不然,Ki無左孩子,即Ki一定是葉子。所以徹底二叉樹中編號i>n/2的結點一定是葉結點。函數
③若2i+1≤n,則Ki的右孩子的編號是2i+1;不然,Ki無右孩子。ui
注:ki(0≤i≤n)知足數組下標時,則可能的左右孩子分別爲2i+一、2i+2。spa
利用堆頂記錄的是最大關鍵字這一特性,每一輪取堆頂元素放入有序區,就相似選擇排序每一輪選擇一個最大值放入有序區,能夠把堆排序當作是選擇排序的改進。3d
不斷重複此二、3步驟直到有序區的元素個數爲n-1,則整個排序過程完成。blog
//最難理解的地方排序
以下圖:遞歸
2.初始化堆
從最後一個非葉子節點i(i=n/2,n爲節點個數)開始,將以i爲根節點的二叉樹經過篩選調整爲堆。以第一張圖爲例,編號順序爲八、七、6...1。
從最後一個非葉子節就保證了篩選算法的正確性,由於篩選算法的目標是一個全部子樹都爲堆的徹底二叉樹。
package sort; import java.util.Arrays; import util.MathUtil; /** * 經過大頂堆實現堆排序,升序排序 * */ public class HeapSort { public static void main(String[] args) { int[] arr={9,6,12,32,23,11,2,100,85}; sort(arr); System.out.println(Arrays.toString(arr)); } //這裏將i定義爲徹底二叉樹的根 //將徹底二叉樹調整爲大頂堆,前提是二叉樹的根的子樹已經爲大頂堆。 public static void adjustHeap(int[]a ,int i,int size){ int lChild=2*i+1; //左孩子 int rChild=2*i+2; //又孩子 int max=i; //臨時變量 if(i<size/2){ //若是i是葉子節點就結束 if(lChild<size&&a[max]<a[lChild]) max=lChild; if(rChild<size&&a[max]<a[rChild]) max=rChild; if(max!=i){ MathUtil.swap(a, max, i);//交換後破環了子樹的堆結構 adjustHeap(a, max, size);//遞歸,調節子樹爲堆 } } } //創建堆,堆是從下往上創建的,由於adjustHeap函數是創建在子樹已經爲大頂堆。 public static void buildHeap(int[]a,int size){ for(int i=size/2;i>=0;i--){//從最後一個非葉子節點,才能構成adjustHeap操做的目標二叉樹 adjustHeap(a, i, size); } } //將數組分爲兩部分,一部分爲有序區,在數組末尾,另外一部分爲無序區。堆屬於無序區 public static void sort(int[] arr){ int size=arr.length; buildHeap(arr, size); for(int i=size-1;i>0;i--){//i爲無序區的長度,通過以下兩步,長度遞減 //堆頂即下標爲0的元素 MathUtil.swap(arr, i, 0);//1.每次將堆頂元素和無序區最後一個元素交換,即將無序區最大的元素放入有序區 adjustHeap(arr, 0, i); //2.將無順區調整爲大頂堆,即選擇出最大的元素。 } } }