堆排序
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
算法描述
- 将初始待排序关键字序列 (R1,R2….Rn) 构建成大顶堆,此堆为初始的无序区;
- 将堆顶元素 R[1] 与最后一个元素 R[n] 交换,此时得到新的无序区 (R1,R2,……Rn-1) 和新的有序区 (Rn),且满足 R[1, 2… n-1] <= R[n];
- 由于交换后新的堆顶 R[1] 可能违反堆的性质,因此需要对当前无序区 (R1, R2, …… Rn-1) 调整为新堆,然后再次将 R[1] 与无序区最后一个元素交换,得到新的无序区 (R1, R2 …. Rn-2) 和新的有序区 (Rn-1, Rn)。不断重复此过程直到有序区的元素个数为 n-1,则整个排序过程完成。
算法分析
- 时间复杂度:
- 最佳情况:T(n) = O(nlogn)
- 最差情况:T(n) = O(nlogn)
- 平均情况:T(n) = O(nlogn)
- 空间复杂度:O(1)
- 稳定性:不稳定
代码实现
public class HeapSort {
public static void heapSort(int[] arr) {
if (arr == null || arr.length <= 1) return;
// 建堆。
buildHeap(arr);
int len = arr.length;
while (len > 1) {
// 把堆顶和最后一个元素交换。
swap(arr, 0, len - 1);
// 交换完之后,逻辑上去掉最后一个元素。
len--;
// 重新调整堆的顺序。
heapfy(arr, 0 , len);
// 把每一趟排序的结果也输出一下。
print(arr);
}
}
private static void buildHeap(int[] arr) {
// 最后一个非叶子结点:2i + 1 >= arr.length --> i >= (arr.length - 1) / 2
for (int i = (arr.length - 1) / 2 - 1; i >= 0; i--) {
heapfy(arr, i, arr.length);
}
}
// 调整堆的顺序,保持大顶堆。
private static void heapfy(int[] arr, int i, int len) {
while (true) {
int maxPostion = i;
int leftChild = 2 * i + 1; // 左孩子索引。
int rightChild = 2 * i + 2; // 右孩子索引。
// 若左孩子大于最大值,则更新最大值。
if (leftChild < len && arr[leftChild] > arr[maxPostion]) {
maxPostion = leftChild;
}
// 若右孩子大于最大值,则更新最大值。
if (rightChild < len && arr[rightChild] > arr[maxPostion]) {
maxPostion = rightChild;
}
if (maxPostion == i) {
break; // 若已经是大顶堆了,则退出循环。
} else {
swap(arr, i, maxPostion); // 若不是大顶堆,则交换位置。
i = maxPostion;
}
}
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] =temp;
}
public static void main(String[] args) {
int[] arr = {6, 9, 1, 4, 5, 8, 7, 0, 2, 3};
System.out.print("排序前: ");
print(arr);
heapSort(arr);
System.out.print("排序后: ");
print(arr);
}
// 打印数组
public static void print(int[] arr) {
if (arr == null) return;
for(int i : arr) {
System.out.print(i + " ");
}
System.out.println();
}
}
/*
排序前: 6 9 1 4 5 8 7 0 2 3
8 6 7 4 5 1 3 0 2 9
7 6 3 4 5 1 2 0 8 9
6 5 3 4 0 1 2 7 8 9
5 4 3 2 0 1 6 7 8 9
4 2 3 1 0 5 6 7 8 9
3 2 0 1 4 5 6 7 8 9
2 1 0 3 4 5 6 7 8 9
1 0 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
排序后: 0 1 2 3 4 5 6 7 8 9
*/