堆排序整體思路:
1. 初始化最大堆 2. push堆頂元素倒最後,並將堆長度減一,直到堆長度爲0
首先幾個關於堆的知識:
1. 堆就是完全二叉樹, 最大堆的特性: 任意的父節點總比其兩個子節點大.
2. 堆排序原理:
- 先將數組整理成一個最大堆. (最大堆就是升序排列,最小堆就是降序排列,)
- 開始push堆頂元素,將最後一個元素放入堆頂,並重新向下整理堆,直到堆長度爲0 (數組中實現就是第一個元素和最後一個元素交換位置)
- 整理一遍堆的時間複雜度是 O(logn), 第一次整理了n/2次,之後每次push操作又整理了n-1次. 故整體時間複雜度約爲 O(n*logn)
3.二叉樹的節點轉換公式: 如果有節點下標爲n,則其左子節點下標爲 2n,右子節點下標爲2n+1,父節點下標爲 n/2
Java具體實現:
package arithmetic.sort;
import java.util.Arrays;
/**
* 堆排序 簡潔優雅版
* @author jiangfeng 2019/7/29 12:45
*/
public class HeapSort2 {
public static void main(String[] args) {
int[] a = {24, 10, 5, 1, 2, 24, 5, 1, 2, 5, 7, 8, 5, 3, 7, 63, 9, 0, 42, 3, 8, 24, 1};
//int [] a = { 5, 4, 3, 2, 1 };
long start = System.currentTimeMillis();
heapSort(a);
System.out.println(Arrays.toString(a)); // 1ms
//System.out.println(JsonOutput.toJson(a));//gson裏的 json output轉換那個很慢. 41ms
//System.out.println(JSON.toJSONString(a));//序列化更慢了 163ms
System.out.println("用時:" + (System.currentTimeMillis() - start));
}
private static void heapSort(int[] a) {
// 1.初始化最大堆,從倒數第二層開始初始化
for (int i = a.length/2; i >=0; i--) {
sortDownHeap(a,a.length,i);
}
// 2. push堆頂元素倒最後,並且重新建立最大堆
for (int i = a.length-1; i >=0 ; i--) {
swap(a,0,i);
sortDownHeap(a,i-1,0);
}
}
/**
* 最大堆向下調整
* @param a
* @param length 堆的大小,邊界
* @param i 需要調整順序的元素
*/
private static void sortDownHeap(int[] a, int length, int i) {
// 最大堆的特性: 任意的父節點總比其兩個子節點大.
// 二叉樹節點轉換: 如果有節點下標爲n,則其左子節點下標爲 2n,右子節點下標爲2n+1,父節點下標爲 n/2
int t = i+1; //便於理解 數組的下標序號和數組的序號的轉換. 下文也需注意這點: 數組實際下標 = 堆下標 - 1;
while (2*t <= length) { // 該節點不是最後一層,存在左子節點
int tmpMax = t;// 記錄父子節點中的最大下標
if (a[t - 1] < a[2 * t - 1]) { // 父節點和左子節點比較出最大值
tmpMax = 2 * t;
}
if (a[tmpMax - 1] < a[2 * t]) { // 上次比較中較大的和右子節點比較
tmpMax = 2 * t + 1;
}
if (tmpMax != t) { // 如果當前根節點不是最大值,則執行交換.
swap(a, t - 1, tmpMax - 1);
t = tmpMax;// 繼續向下調整
} else {
return;// 否則停止調整
}
}
}
private static void swap(int[] a, int i, int j) {
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}