摘要:
作為選擇排序的改進版,堆排序可以把每一趟元素的比較結果保存下來,以便我們在選擇最小/大元素時對已經比較過的元素做出相應的調整。
堆排序是一種樹形選擇排序,在排序過程中可以把元素看成是一顆完全二叉樹,每個節點都大(小)於它的兩個子節點,當每個節點都大於等於它的兩個子節點時,就稱為大頂堆,也叫堆有序; 當每個節點都小於等於它的兩個子節點時,就稱為小頂堆。



(大頂堆(有序堆)) (小頂堆)
算法思想(以大頂堆為例):
1.將長度為n的待排序的數組進行堆有序化構造成一個大頂堆
2.將根節點與尾節點交換並輸出此時的尾節點
3.將剩余的n -1個節點重新進行堆有序化
4.重復步驟2,步驟3直至構造成一個有序序列
假設待排序數組為[20,50,10,30,70,20,80]
構造堆
在構造有序堆時,我們開始只需要掃描一半的元素(n/2-1 ~ 0)即可,為什么?
因為(n/2-1)~0的節點才有子節點,如圖1,n=8,(n/2-1) = 3 即3 2 1 0這個四個節點才有子節點


(圖1:初始狀態)
所以代碼4~6行for循環的作用就是將3 2 1 0這四個節點從下到上,從右到左的與它自己的子節點比較並調整最終形成大頂堆,過程如下:
第一次for循環將節點3和它的子節點7 8的元素
進行比較,最大者作為父節點(即元素60作為父節點)
【紅色表示交換后的狀態】


第二次
for循環將節點2和它的子節點5 6的元素進行比較,最大者為父節點(元素80作為父節點)


第三次
for循環將節點1和它的子節點3 4的元素進行比較,最大者為父節點(元素70作為父節點)


第四次
for循環將節點0和它的子節點1 2的元素進行比較,最大者為父節點(元素80作為父節點)


(注意這里,元素20和元素80交換后,20所在的節點還有子節點,所以還要再和它的子節點5 6的元素進行比較,
這就是28行代碼 i = j 的原因)
至此有序堆已經構造好了!如下圖:


調整堆
下面進行while循環
(1)堆頂元素80和尾40交換后-->調整堆


(2)堆頂元素70和尾30交換后-->調整堆


(3)堆頂元素60尾元素20交換后-->調整堆


(4)其他依次類推,最終已排好序的元素如下:

代碼實現
public class HeapSort { private static void heapSort(int[] arr) { int len = arr.length -1; for(int i = len/2 - 1; i >=0; i --){ //堆構造 heapAdjust(arr,i,len); } while (len >=0){ swap(arr,0,len--); //將堆頂元素與尾節點交換后,長度減1,尾元素最大 heapAdjust(arr,0,len); //再次對堆進行調整 } } public static void heapAdjust(int[] arr,int i,int len){ int left,right,j ; while((left = 2*i+1) <= len){ //判斷當前父節點有無左節點(即有無孩子節點,left為左節點) right = left + 1; //右節點 j = left; //j"指針指向左節點" if(j < len && arr[left] < arr[right]) //右節點大於左節點 j ++; //當前把"指針"指向右節點 if(arr[i] < arr[j]) //將父節點與孩子節點交換(如果上面if為真,則arr[j]為右節點,如果為假arr[j]則為左節點) swap(arr,i,j); else //說明比孩子節點都大,直接跳出循環語句 break; i = j; } } public static void swap(int[] arr,int i,int len){ int temp = arr[i]; arr[i] = arr[len]; arr[len] = temp; } public static void main(String[] args) { int array[] = {20,50,20,40,70,10,80,30,60}; System.out.println("排序之前:"); for(int element : array){ System.out.print(element+" "); } heapSort(array); System.out.println("\n排序之后:"); for(int element : array){ System.out.print(element+" "); } } }
輸出:
排序之前: 20 50 20 40 70 10 80 30 60 排序之后: 10 20 20 30 40 50 60 70 80
堆排序主要在於理解堆的構造過程和在輸出最大元素后如何對堆進行重新調整,借助IDE工具的調試功能可以很好的幫助理解整個排序過程。