1、概念
堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。(维基百科)
2、运行过程
1、思想概述
若以升序排序说明,把数组转换成最大堆(Max-Heap Heap),这是一种满足最大堆性质(Max-Heap Property)的二叉树:对于除了根之外的每个节点i, A[parent(i)] ≥ A[i]。
重复从最大堆取出数值最大的结点(把根结点和最后一个结点交换,把交换后的最后一个结点移出堆),并让残余的堆维持最大堆性质。
2、堆的操作
在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:
- 最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
- 创建最大堆(Build Max Heap):将堆中的所有数据重新排序
- 堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
3、运行过程
1、创建一个最大(小)堆H;
2、把堆首和堆尾元素互换;
3、把堆的大小减1,重新构造一个最大(小)堆;
4、重复步骤2、3,直到堆的大小减少为1。
(我们以最大堆为例)
image
3、代码
java
import java.util.Arrays;
public class HeapSort {
private int[] arr;
public HeapSort(int[] arr) {
this.arr = arr;
}
/**
* 堆排序的主要入口方法,共两步。
*/
public void sort() {
/*
* 第一步:将数组堆化
* beginIndex = 第一个非叶子节点。
* 从第一个非叶子节点开始即可。无需从最后一个叶子节点开始。
* 叶子节点可以看作已符合堆要求的节点,根节点就是它自己且自己以下值为最大。
*/
int len = arr.length - 1;
int beginIndex = (arr.length >> 1)- 1;
for (int i = beginIndex; i >= 0; i--)
maxHeapify(i, len);
/*
* 第二步:对堆化数据排序
* 每次都是移出最顶层的根节点A[0],与最尾部节点位置调换,同时遍历长度 - 1。
* 然后从新整理被换到根节点的末尾元素,使其符合堆的特性。
* 直至未排序的堆长度为 0。
*/
for (int i = len; i > 0; i--) {
swap(0, i);
maxHeapify(0, i - 1);
}
}
private void swap(int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
/**
* 调整索引为 index 处的数据,使其符合堆的特性。
*
* @param index 需要堆化处理的数据的索引
* @param len 未排序的堆(数组)的长度
*/
private void maxHeapify(int index, int len) {
int li = (index << 1) + 1; // 左子节点索引
int ri = li + 1; // 右子节点索引
int cMax = li; // 子节点值最大索引,默认左子节点。
if (li > len) return; // 左子节点索引超出计算范围,直接返回。
if (ri <= len && arr[ri] > arr[li]) // 先判断左右子节点,哪个较大。
cMax = ri;
if (arr[cMax] > arr[index]) {
swap(cMax, index); // 如果父节点被子节点调换,
maxHeapify(cMax, len); // 则需要继续判断换下后的父节点是否符合堆的特性。
}
}
/**
* 测试用例
*
* 输出:
* [661,571,467,565,537,654,21,732,133,157,815,512,54,257,469,511,97,53,513,259,3,676,396,30,719,553,34,33,8,57, 69]
*/
public static void main(String[] args) {
int[] arr = new int[] {661,571,467,565,537,654,21,732,133,157,815,512,54,257,469,511,97,53,513,259,3,676,396,30,719,553,34,33,8,57, 69};
new HeapSort(arr).sort();
System.out.println(Arrays.toString(arr));
}
}
4、性能
堆排序的平均时间复杂度为O(nlog n),空间复杂度为O(1),是不稳定的排序算法。
参考:
十大经典排序算法(动图演示)
网友评论