2021/02/27
堆排序的基本思想
堆排序(HeapSort)是利用 二叉堆
进行排序的排序算法,升序排序使用最大堆,降序排序使用最小堆。
本文以升序排序为例。
堆排序的步骤如下:
- 用待排序列构建一个大顶堆,
n=heap.size() - 循环如下操作,直到
n==0- 将堆顶元素与第
n个元素交换,n=n−1 - 对前
n个元素重新调整为大顶堆
- 将堆顶元素与第
也就是说,因为大顶堆的堆顶始终是最大元素,每次将最大元素经过交换后放在堆的最后,经过 n−1 次调整后,就可以得到一个升序序列。
例如对一个无序序列:11, 22, 11, 3, 12, 15, 2, 21, 47 进行升序排序,具体过程如下:
首先构建一个大顶堆,
然后堆顶元素47和堆最后元素3交换,并将 除47 的所有元素重新调整为大顶堆,
交换 22 和 3,并将 除22,47 的所有元素重新调整为大顶堆,
交换 21 和 2,并将 除21,22,47 的所有元素重新调整为大顶堆,
交换 15 和 2,并将 除15,21,22,47 的所有元素重新调整为大顶堆,
交换 12 和 3 ,并将 除12,15,21,22,47 的所有元素重新调整为大顶堆,
继续操作,直到只剩一个元素时就可以停止了,
最终的堆就是排序的结果 2, 3, 11 ,11 ,12 ,15 ,21 ,22 ,47。
以上就是堆排序的过程演示,下面用代码实现。
代码实现
堆排序的交换操作,相当于堆的删除操作,但是并没有真正的将堆顶元素移除。堆的删除操作后,只需要对堆顶元素进行下沉操作即可重新调整为大顶堆,而构建堆也是只用到下沉操作,因此堆排序的实现只需要用到堆的下沉操作。
Java 代码实现如下:
public class HeapSort {
/**
* 堆排序(升序)
*
* @param values 待排序数组
*/
public static void heapSort(int[] values) {
for (int i = (values.length - 2) / 2; i >= 0; i--) {
downAdjust(values, i, values.length);
}
for (int i = values.length - 1; i > 0; i--) {
int temp = values[0];
values[0] = values[i];
values[i] = temp;
downAdjust(values, 0, i);
}
}
/**
* 堆节点下沉
*
* @param values 堆数组
* @param index 下沉节点索引
* @param length 堆调整的实际有效长度
*/
static void downAdjust(int[] values, int index, int length) {
int childIndex = 2 * index + 1;
int temp = values[index];
while (childIndex < length) {
if (childIndex + 1 < length && values[childIndex + 1] > values[childIndex]) {
childIndex++;
}
if (temp > values[childIndex]) {
break;
}
values[index] = values[childIndex];
index = childIndex;
childIndex = 2 * index + 1;
}
values[index] = temp;
}
}
算法分析
- 堆排序是不稳定的
- 堆排序的时间复杂度为
O(nlogn)