一、堆排序基本介紹
- 堆排序是利用堆這種數據結構而設計的一種排序算法,堆排序是一種選擇排序,它的最壞,最好,平均時間複雜度均爲O(nlogn),它也是不穩定排序。
- 堆是具有以下性質的完全二叉樹:每個結點的值都大於或等於其左右孩子結點的值,稱爲大頂堆, 注意 : 沒有要求結點的左孩子的值和右孩子的值的大小關係。
- 每個結點的值都小於或等於其左右孩子結點的值,稱爲小頂堆
- 大頂堆舉例說明
大頂堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2] , i 對應第幾個節點,i從0開始編號
6. 小頂堆舉例說明
小頂堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2] , i 對應第幾個節點,i從0開始編號
二、基本思想
- 將待排序序列構造成一個大頂堆
- 此時,整個序列的最大值就是堆頂的根節點。
- 將其與末尾元素進行交換,此時末尾就爲最大值。
- 然後將剩餘n-1個元素重新構造成一個堆,這樣會得到n個元素的次小值。如此反覆執行,便能得到一個有序序列了。
可以看到在構建大頂堆的過程中,元素的個數逐漸減少,最後就得到一個有序序列了.
三、思路步驟
思路
- 將無序序列構建成一個堆,根據升序降序需求選擇大頂堆或小頂堆;
- 將堆頂元素與末尾元素交換,將最大元素"沉"到數組末端;
- 重新調整結構,使其滿足堆定義,然後繼續交換堆頂元素與當前末尾元素,反覆執行調整+交換步驟,直到整個序列有序。
步驟一
構造初始堆。將給定無序序列構造成一個大頂堆(一般升序採用大頂堆,降序採用小頂堆)。
-
假設給定無序序列結構如下
-
此時我們從最後一個非葉子結點開始(葉結點自然不用調整,第一個非葉子結點 arr.length/2-1=5/2-1=1,也就是下面的6結點),從左至右,從下至上進行調整。
-
找到第二個非葉節點4,由於[4,9,8]中9元素最大,4和9交換。
-
這時,交換導致了子根[4,5,6]結構混亂,繼續調整,[4,5,6]中6最大,交換4和6。
此時,我們就將一個無序序列構造成了一個大頂堆。
步驟二
將堆頂元素與末尾元素進行交換,使末尾元素最大。然後繼續調整堆,再將堆頂元素與末尾元素交換,得到第二大元素。如此反覆進行交換、重建、交換。
- 將堆頂元素9和末尾元素4進行交換
- 重新調整結構,使其繼續滿足堆定義
- 再將堆頂元素8與末尾元素5進行交換,得到第二大元素8.
- 後續過程,繼續進行調整,交換,如此反覆進行,最終使得整個序列有序
四、代碼實現
import java.util.Arrays;
// 堆排序
public class HeapSort {
public static void main(String[] args) {
//速度測試
long begin = System.currentTimeMillis();
int[] array = {3, 6, 2, 4, 5, 7, 1};
// int[] array = new int[8];
// for (int i = 0; i < array.length; i++) {
// array[i] = (int) (Math.random() * 8000000);
// }
heapSort(array);
long end = System.currentTimeMillis();
System.out.println("用時:" + (end - begin));
System.out.println(Arrays.toString(array));
}
public static void heapSort(int[] array) {
int temp = 0;
//1.將無序序列構建一個堆,根據升序降序需求選擇大頂堆或小頂堆
for (int i = array.length / 2 - 1; i >= 0; i--) {
adjustHeap(array, i, array.length);
}
// System.out.println("完全二叉樹"+Arrays.toString(array));
//2.把對頂元素與末尾元素交換,將最大的元素沉到數組末端;
//3.重新調整結構,使其滿足堆定義
for (int j = array.length - 1; j > 0; j--) {
//交換
temp = array[j];
array[j] = array[0];
array[0] = temp;
adjustHeap(array, 0, j);
}
// System.out.println("排序後"+Arrays.toString(array));
}
/**
* 將一個數組(二叉樹)調整爲大頂堆
*
* @param array 待調整的數組
* @param index 非葉子節點在數組中的索引
* @param length 表示對多少個元素繼續調整
*/
public static void adjustHeap(int[] array, int index, int length) {
//先取出當前元素的值,保存到當前臨時變量
int temp = array[index];
//k = index * 2 + 1 是index結點的左子節點
for (int k = index * 2 + 1; k < length; k = k * 2 + 1) {
//左子節點的值小於右子節點的值
if (k + 1 < length && array[k] < array[k + 1]) {
//k 指向右子節點
k++;
}
// 如果子節點的值大於父結點的值則進行值的交換
if (array[k] > temp) {
array[index] = array[k];
//index指向 k ,繼續循環比較
index = k;
} else {
break;
}
//把temp放到調整後的位置
array[index] = temp;
}
}
}