在一組無序數組中,比如{1,9,8,2,7,3,6,4,5}
將數組看做是一個堆
,也可以用二叉樹
來表示
但是這個堆現在還不是大頂堆
,大頂堆的特點是父節點
永遠大於左右子節點
第一步需要將這個堆構建成大頂堆
構建前需要知道的幾點:
-
二叉樹的最后一個
非葉子節點
,計算公式是:array.length / 2 - 1
-
非葉子節點
都是相鄰的,這就是為什么buildMaxHeap
方法中的i
的步進方式是減1
-
父節點
的左子節點
的計算公式是:2 * i + 1
-
父節點
的右子節點
的計算公式是:2 * i + 2
-
buildMaxHeap
方法的length
參數意義:因為構建大頂堆
后,根節點
就成了最大值,此時將根節點
和數組尾元素
交換,就能將最大值移動到數組末尾,那么第一大的值已經確定,現在需要確定第二大的值,那么就需要在剩下的元素當中再次構建大頂堆
,所以就需要控制后續構建大頂堆
時的數組長度,也就是length
/** * 構建大頂堆 * * @param array 原始數組 * @param length 需要構建的長度 */ private static void buildMaxHeap(int[] array, int length) { //從最后一個非葉子節點開始 for (int i = length / 2 - 1; i >= 0; i--) { adjustHeap(array, i, length); } } /** * 調整大頂堆 * * @param array 被調整數組 * @param i 非葉子節點 * @param length 需要調整的長度 */ private static void adjustHeap(int[] array, int i, int length) { //獲取當前非葉子節點的值 int temp = array[i]; //依次遍歷非葉子節點的左子節點 for (int j = 2 * i + 1; j < length; j = 2 * j + 1) { //讓j指向左右子節點較大的哪個 if (j + 1 < length && array[j] < array[j + 1]) { j++; } //如果子節點>父節點 if (array[j] > temp) { //讓當前非葉子節點的值等於子節點的值 array[i] = array[j]; //讓當前非葉子節點的下標指向當前字節點的下標 i = j; } else { //因為大頂堆是從下到上構建的,所以如果父節點是最大的那個的話就可以直接退出循環 break; } //讓大的子節點等於之前非葉子節點的值 array[j] = temp; } }
當無序數組第一次執行完buildMaxHeap
方法后,已經可以確定根節點就是最大值,然后將根節點的值與元素末尾的值交換
然后循環這個過程
/** * 獲取前n個最大的數 * * @param array 原始數組 * @param n 前n個 */ public static int[] heapSort(int[] array, int n) { int size = n; //第一次構建大頂堆 int length = array.length; buildMaxHeap(array, length); //此時頂端是數組中最大的節點,將頂端與數組末尾交換,然后在剩下的數組中再次構建大頂堆 while (n > 0 && n <= length) { // 交換首尾元素 int temp = array[0]; array[0] = array[length - 1]; array[length - 1] = temp; n--; length--; buildMaxHeap(array, length); } int[] result = new int[size]; System.arraycopy(array, array.length - size, result, 0, size); return result; }
重點看這個地方
while (n > 0 && n <= length) { // 交換首尾元素 int temp = array[0]; array[0] = array[length - 1]; array[length - 1] = temp; n--; length--; buildMaxHeap(array, length); }
-
交換根節點和數組末尾元素后,意思就是已經確定最大值,然后將
n
減1,length
減1,n
用來控制構建大頂堆的次數,構建n
次,就能確定前n
個最大值 -
然后再次構建大頂堆,直到
n=0
跳出循環
while
循環結束,那么數組的后n項,已經是排序好了的
假如n=3
那么結果就是
假如n=9
那么結果就是{1,2,3,4,5,6,7,8,9}