1、第 1 个版本的基于堆排序的排序算法
思路:借助最大堆这个数据结构,依次往里面放元素,然后再依次倒序取出。
性能比较:100 万个元素。
合并排序,快速排序,3路快速排序,堆排序1
(1)随机
(2)近乎有序
(3)含有大量相同元素的数组
堆排序的时间复杂度是 O(nlogn)。
代码实现:
/**
* 第 1 个版本的堆排序算法
* Created by liwei on 17/5/15.
*/
public class HeapSort1 implements ISortAlgorithm {
@Override
public String getName() {
return "第 1 个版本的堆排序算法";
}
@Override
public void sort(int[] arr) {
int length = arr.length;
MaxHeap maxHeap = new MaxHeap(length);
for (int i = 0; i < length; i++) {
maxHeap.insert(arr[i]);
}
for (int i = length - 1; i >= 0; i--) {
arr[i] = maxHeap.extractMax();
}
}
}
2、第 2 个版本的基于堆排序的排序算法
理解什么是Heapify?(尝试将一整个数组构建成一个堆的更好的方式,因为此时无须借助额外的空间就完成了最大堆的构建)
关键的部分:
1、所有的叶子节点就是一个最大堆,此时每个堆中的元素只有1个;
2、重要性质:当我们的索引从 1 开始计数的前提下,第 1 个非叶子的节点的索引是 index/2(我们可以使用数学归纳法证明一下),如何让它满足堆的性质呢?shift down 就可以了。
3、从 index/2 递减到根(index=1的时候)依次去完成 shift down,一开始就排除了 length/2 这么多元素。
下面的代码是关键,须要重视!!!
/**
* 传递一个数组,形成一个最大堆
* 理解 heapify 是关键
*
* @param arr 待排序的数组元素
*/
public MaxHeap(int[] arr) {
int length = arr.length;
data = new int[length + 1];
for (int i = 0; i < length; i++) {
data[i + 1] = arr[i];
}
// 这一步赋值千万别忘了
count = length;
// 理解这一步是关键 heapify
for (int i = length / 2; i >= 1; i--) {
shiftDown(i);
}
}
这样,我们就可以写出我们的第 2 个使用堆排序的算法了,直接把数组传到最大堆这个数据结构里面。
/**
* 第 2 个版本的堆排序算法
* Created by liwei on 17/5/15.
*/
public class HeapSort2 implements ISortAlgorithm {
@Override
public String getName() {
return "第 2 个版本的堆排序算法";
}
@Override
public void sort(int[] arr) {
MaxHeap maxHeap = new MaxHeap(arr);
int length = arr.length;
for (int i = length - 1; i >= 0; i--) {
arr[i] = maxHeap.extractMax();
}
}
}
重要结论:堆排序在整体上的性能不如归并排序和快速排序。堆这种数据结构更多的时候用于动态数据的维护。
一个数学结论:将 n 个元素逐一插入到一个空堆中,时间复杂度是 O(nlogn)。Heapify 的过程,时间复杂度是 O(n)。
HeapSort2 会快一点的原因是:一上来我们从 n/2 这个地方开始,– 去循环,排除了 n/2 个元素,所以效率肯定比第 1 种好。
可是这两种基于堆的排序算法,我们在堆排序的过程中,使用了额外的空间(MaxHeap 中的数组),使用了 O(n) 的空间复杂度。那么不借助额外的空间是不是也可以完成堆排序呢?