很久以前有人問我大數據取n個最大數或者最小數用什么方法最效率,開始我沒想到,后面想到了堆排序。
在介紹堆排序之前,首先需要說明一下,堆是個什么玩意兒。
堆是一棵順序存儲的完全二叉樹,出于簡便起見,完全二叉樹通常采用數組而不是鏈表存儲,其存儲結構如下:
int[] array = new int[] { 0,1,2,3..n-1 };長度為n
對于tree[i],有如下特點:
第i個節點的如果有左孩子節點為 2i+1,如果有右孩子為2i+2
在完全二叉樹的基礎上,如果中每個結點都不大于其子結點,這樣的堆稱為小頂堆。每個結點的都不小于其子結點,這樣的堆稱為大頂堆。
那么取n個極值的問題通過堆排序可以轉換成大頂堆或者小頂堆n次移除根節點再重新規劃的過程。
現在對于堆排序來說,就是把待排序的一堆無序的數,整理成一個大頂堆,或者小頂堆。
其基本思想為(大頂堆):
1.將初始待排序關鍵字序列(R1,R2....Rn)構建成大頂堆,此堆為初始的無須區;
2.將堆頂元素R[1]與最后一個元素R[n]交換,此時得到新的無序區(R1,R2,......Rn-1)和新的有序區(Rn),且滿足R[1,2...n-1]<=R[n];
3.由于交換后新的堆頂R[1]可能違反堆的性質,因此需要對當前無序區(R1,R2,......Rn-1)調整為新堆,然后再次將R[1]與無序區最后一個元素交換,得到新的無序區(R1,R2....Rn-2)和新的有序區(Rn-1,Rn)。不斷重復此過程直到有序區的元素個數為n-1,則整個排序過程完成。
'''
public class HeapSort {
public static void main(String[] args) {
int[] array = new int[] { 2, 1, 4, 3, 6, 5, 8, 7 };
sort(array);
System.out.println(Arrays.toString(array));
}
public static void sort(int[] array) {
for (int i = array.length / 2 - 1; i >= 0; i--) {
adjustHeap(array, i, array.length);
}
// 上述邏輯,建堆結束
// 下面,開始排序邏輯
for (int j = array.length - 1; j > 0; j--) {
// 每次找到最大的也就是數組的第0個對象與當前進入排序區的數組末尾對象交換,然后重新排序,完成后j減1,不斷遞歸
swap(array, 0, j);
adjustHeap(array, 0, j);
}
}
public static void adjustHeap(int[] array, int i, int length) {
int temp = array[i];
for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
// 讓k先指向子節點中最大的節點
if (k + 1 < length && array[k] < array[k + 1]) {
k++;
}
// 如果發現子節點更大,則進行值的交換
if (array[k] > temp) {
swap(array, i, k);
// 如果子節點更換了,那么,以子節點為根的子樹會不會受到影響呢?
// 所以,循環對子節點所在的樹繼續進行判斷否則就直接終止循環了
i = k;
} else {
break;
}
}
}
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
'''