八大排序-堆排序(手寫堆排序)
原理
以最大堆爲例,利用最大堆結構的特點:每個最大堆的根節點必然是數組中最大的元素,構建一次最大堆即可獲取數組中最大的元素。剔除最大元素後,反覆構建餘下數字爲最大堆獲取根元素最終保證數組有序。
以上都是廢話,建議直接看圖
最大堆定義
- 最大堆圖示
- 最大堆數組
滿足父節點大於或等於左右子節點即爲最大堆,最大堆二叉樹以及對應數組如上圖。
堆排序流程
1.一趟堆排序
以數組int n[] = { 6, 5, 2, 7, 3, 9, 8 }爲例:
步驟
一、構建最大堆:
從最後一個非葉子節點(計算公式爲n.length/2-1,可自行驗證)開始,從後往前進行調整構建,調整的規則是:若子節點大於父節點,則將子節點中較大的節點元素與父節點交換。
1.調整節點2(數組索引2),對比子節點9和8,再對比較大子節點9和父節點2,將9和2進行交換;
2.調整節點5(數組索引1),對比子節點7和3,將7和5進行交換;
3.調整節點6(素組索引0),對比子節點7和9,將6和9進行交換;
二、取出最大堆數組的第一位根元素與數組末位元素交換:
2.循環構建最大堆
根據上述構建最大堆的原理可以得出堆排序的完整過程
- 第1趟堆排序:
- 第2趟堆排序:
- 第3趟堆排序:
- 第4趟堆排序:
- 第5趟堆排序:
- 第6趟堆排序:
編碼實踐
public class Test {
public static void main(String[] args) {
int n[] = { 6, 5, 2, 7, 3, 9, 8 };
heapsort(n);
System.out.print("堆排序結果:");
for (int m : n) {
System.out.print(m + " ");
}
}
/**
* 堆排序
* @param n 待排序數組
*/
public static void heapsort(int n[]) {
for (int i = n.length - 1; i >= 1; i--) {
buildHeap(n, i);
swap(n, 0, i);
}
}
/**
*
* @param n 待排序數組
* @param end 待排序數組末位下標
*/
public static void buildHeap(int n[], int end) {
int len = end + 1;
for (int i = len / 2 - 1; i >= 0; i--) {
//堆中i節點對應的左右子節點l和r
int l = 2 * i + 1, r = l + 1;
//指向較大數節點的指針
int p = l;
if (r <= len - 1 && n[l] < n[r]) {
p = r;
}
if (n[i] < n[p]) {
swap(n, i, p);
}
}
}
/**
*
* @param n 待排序數組
* @param i 待交換數字數組下標
* @param j 待交換數字數組下標
*/
private static void swap(int n[], int i, int j) {
n[i] ^= n[j];
n[j] ^= n[i];
n[i] ^= n[j];
}
- 結果
堆排序結果:2 3 5 6 7 8 9
編碼說明
一、heapsort堆排序方法
for循環(注意循環次數比數組長度小1,原因最低2個數構成最大堆)
i.根據數組長度構建最大堆;
ii.交換數組首位(最大堆根節點)與數組末位;
二、buildHeap構建最大堆
1.聲明數組長度len;
2.for循環從最後一個非葉子結點開始往回遍歷到根節點;
i.找到當前非葉子結點的左右子節點下標;
ii.聲明較大數指針,默認指向左子節點;
iii.對比左右子節點,若右子節點存在且右大於左則指向右子節點;
iv.對比交換較大的子節點和父節點。
結語
本篇以最簡單的圖文形式講解八大排序之一的堆排序的核心思想和具體實現,堆排序和快速排序均爲相對其他排序出現頻率最高的排序算法。