一、基本思路
堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
基本思路:‘将待排元素构造成一个大顶堆,此时整个序列的最大值是堆顶的根节点,将其与末尾元素进行交换,此时末尾就为最大值,然后将剩余n-1个元素重新构造成一个堆,这样会得到n-1个元素的次小值,反复执行,便能得到一个有序序列。
**简单来说:**就是循环执行两步:一、构造大顶堆;二、将根节点元素与末尾元素交换
**举例:**有一序列为:4,6,8,5,9要求按照升序排列
假定初始结构为:
- 构造大顶堆
- 找到第一个非叶子节点,arr.length/2-1=5/2-1=1,也就是6,由于[6,5,9]中,9最大,所以6和9交换。
- 找到第二个非叶子节点4,由于[4,8,9]中,9最大,所以4和9交换。
- 此时发现子根[4,5,6]结果混乱,所以继续调整,由于[4,5,6]中,6最大,所以4和6交换.
此时就构造成了一个大顶堆。
- 找到第一个非叶子节点,arr.length/2-1=5/2-1=1,也就是6,由于[6,5,9]中,9最大,所以6和9交换。
- 将堆顶元素与末尾元素交换,使末尾元素最大。
- 此时在除去9的n-1个元素中,循环执行1、2步操作,知道得到一个有序序列。
二、算法分析
时间:
- 构建堆:
设元素个数为n,则堆的高度k=log(n+1)≈log n,非叶子结点的个数为2^(k-1)-1
假设每个非叶子结点都需要进行调整,则第i层的非叶子结点需要的操作次数为k-i,
第i层共有2(i-1)个结点,则第i层的所有结点所做的操作为k*2(i-1)- i*2^(i-1),
共k-1层非叶子结点,总的操作次数为
化简可得,上式=2^k-k+1,将k=log(n+1)≈log n代入,得n - log n +1,
所以,初始化堆的复杂度为O(n) - 调整堆:
假设根节点和排在最后的序号为m的叶子结点交换,并进行调整,那么调整的操作次数 = 原来m结点所在的层数 = 堆的高度(因为m结点在堆的最后)= log m
共n个结点,调整的总操作次数为
化简可得,上式=log (n-1)! ≈ nlog n
所以,调整堆的复杂度为O(nlog n)
所以,总体复杂度为O(n*log n)
**空间:**可以看到,堆排序的元素构建堆以及调整堆都是在序列上直接进行的,无需另外开辟空间,所以空间复杂度为O(1)。
算法 | 平均时间 | 最好情形 | 最差情形 | 稳定度 | 空间复杂度 | 备注 |
---|---|---|---|---|---|---|
堆排序 | O(n l o g 2 n {log_2{n}} log2n) | O(n l o g 2 n {log_2{n}} log2n) | O(n l o g 2 n {log_2{n}} log2n) | 不稳定 | O(1) |
三、代码实现
import java.util.Arrays;
/**
* @author dankejun
* @create 2020/9/910:21
*/
public class HeapSort {
public static void main(String[] args) {
int[] arr = {4, 6, 8, 5, 9};
heapSort(arr);
}
//堆排序方法
public static void heapSort(int[] arr) {
int temp = 0;
System.out.println("堆排序");
// adjustHeap(arr,1,arr.length);
// System.out.println("第一次:" + Arrays.toString(arr));
//
// adjustHeap(arr,0,arr.length);
// System.out.println("第二次:" + Arrays.toString(arr));
//调整为大顶堆
for (int i = arr.length / 2 - 1; i >= 0; i--) {
adjustHeap(arr,i,arr.length);
}
for (int j = arr.length - 1; j > 0; j--) {
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
adjustHeap(arr,0,j);
}
System.out.println("数组:" + Arrays.toString(arr));
}
//将数组调整为大顶堆
/**
* 将以i对应的非叶子节点的树调整成大顶堆
* @param arr 待调整的数组
* @param i 表示非叶子节点在数组中的索引
* @param length 表示对多少个元素进行调整,逐渐减少
*/
public static void adjustHeap(int[] arr, int i, int length) {
int temp = arr[i];
for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
if (k + 1 < length && arr[k] < arr[k + 1]) {
k++;
}
if (arr[k] > temp) {
arr[i] = arr[k];
i = k;
} else {
break;
}
}
arr[i] = temp;
}
}
测试序列: int arr[] = {4, 6, 8, 5, 9};
测试结果: