简介
堆排序是利用完全二叉树这种数据结构当成顺序存储结构所设计的一种排序算法。
堆排序的思想
- 我不想说那么多理论,单纯的说下堆排序的步骤。
- 步骤一:首先将一个数组的所有元素当成一个二叉树操作。这个操作叫创建堆,目的是对于每一个非叶子节点,形成
父节点 < 左孩子 < 右孩子
这种结构。 - 步骤二:取出根节点,进行输出。然后不断将最后一个子节点放到根节点进行创建堆操作。再取出根节点。以此类推。
- 先看代码,再上图说明。
完全二叉树链式结构和线性结构的换算:如果当前节点是
k
- 父节点是:
(k-1)/2
- 左孩子是:
2*k+1
- 右孩子是:
2*k+2
- 父节点是:
核心代码
public class HeapSort {
public static void main(String[] args) {
int[] array = new int[]{6, 3, 9, 2, 4, 5, 1, 8, 7};
heapSort(array);
}
// 堆排序
public static void heapSort(int[] array) {
// 开始建堆
// 从最后一个非叶子(array.length-1)/2结点开始建
for (int i = (array.length - 1) / 2; i >= 0; i--) {
createHeap(array, array.length, i);
}
// 开始边输出堆顶,边调整堆
int n = array.length; // 表求堆中元素的个数
while (n > 0) {
System.out.print(array[0] + " "); // 根取走
// 最后一个节点放到根上
array[0] = array[n - 1];
n--;
// 重新调整
createHeap(array, n, 0);
}
}
// 需要完成一次建堆的过程
// n:表示堆中有多少个数据
// k:准备进行筛选的节点
public static void createHeap(int[] array, int n, int k) {
int kLeft = 2 * k + 1; // 左孩子
int kRight = 2 * k + 2; // 右孩子
// 1.没有子节点直接return
if (kLeft >= n && kRight >= n) {
// 没有子节点直接return
return;
}
int kLeftValue = Integer.MAX_VALUE;
int kRightValue = Integer.MAX_VALUE;
// 2.如果有左孩子就记录左孩子的值
if (kLeft < n) {
kLeftValue = array[kLeft];
}
// 3.如果有友孩子就记录右孩子的值
if (kRight < n) {
kRightValue = array[kRight];
}
// 4.如果已经满足要求就不用比较和交换了
// 要求:父节点 < 左孩子 < 右孩子
if (array[k] <= kLeftValue && array[k] <= kRightValue) {
return;
}
/*
5.
如果左孩子 < 右孩子
让左孩子和父节点交换
如果左孩子 > 右孩子
让右孩子和父节点交换
*/
if (kLeftValue < kRightValue) {
int t = array[k];
array[k] = array[kLeft];
array[kLeft] = t;
createHeap(array, n, kLeft);
} else {
int t = array[k];
array[k] = array[kRight];
array[kRight] = t;
createHeap(array, n, kRight);
}
}
}
直接对着图说。
上面是
for
循环每一步生成的二叉树图(画这个累死了)- 可以看见从最开始的二叉树的无序,到最后一棵二叉树的1, 2, 5, 3对应的父节点都要比孩子节点小(绿框框标注的)。
然后开始
while
循环- 现在输出根节点,也就是1。
- 接着将最后一个节点4放到根节点,执行创建堆操作。输出2。
- 以此类推,每次输出都是最小的值。
- 排序过后的输出结果:
1, 2, 3, 4, 5, 6, 7, 8, 9
。
不懂的话,对照代码和图多看几遍。
- 我就是不得已才画了个图对照着看的。
优缺点
优点
- 快,和快速排序同等级。比快速排序略低。
- 空间复杂度为
O(1)
,既高效率又节省时间。
缺点
- 维护成本高。堆排序中每次更新都需要重新做一遍堆的维护,大多数情况是没必要的。而且实际开发中数据是频繁变动的。这更带来了浪费问题。
- 这也是为什么内部排序喜欢用快速排序不喜欢用堆排序的原因。
复杂度
时间复杂度
O(n*logn)
空间复杂度
O(1)
稳定度
- 稳定
应用场景
- 当数据源为无序或链式结构的时候,进行二分查找。