简介
堆排序是一种利用堆数据结构实现的排序算法。首先,它将待排序的数组构建成一个大顶堆或小顶堆。然后,通过不断将堆顶元素(最大或最小)与末尾元素交换并重新调整堆,使得数组逐渐有序。
第一步:构建大顶堆/小顶堆
下面就是一个用大顶堆构建的例子:
最后要达成的效果就是每个节点的值都大于或等于其左右孩子节点的值,称为大顶堆。
理解了上面的概念后,继续下一步
第二步:将堆顶元素与末尾元素进行交换
交换过程如下:
需要注意的是,每次交换完毕,能保证末尾元素是最大的,但是交换来的数据不一定是当前最大的,所以需要继续将该节点放入合适的位置,再次构建大顶堆
这样排序就结束了,其实并不复杂。我们只需要完全理解其中的关键点就可以实现:
- 构建:调整每个节点(非叶子节点)至合适的位置,构建大/小顶堆
- 交换:交换末尾元素后,继续将该节点调整
实现(java例子)
利用大顶堆和递归的方式,实现排序
public static void creatBigHeap(int[] a, int len) {
// 将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
// len / 2 - 1 很关键, 无需调整叶子节点,对每个非叶子结点进行调整
for (int i = len / 2 - 1; i >= 0; i--) {
adjustDown(a, i, len);
}
// 将堆顶元素与末尾元素交换。将最大的元素沉到数组末端
for (int j = a.length - 1; j > 0; j--) {
swap(a, 0, j);
// 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序
adjustDown(a, 0, j);
}
}
/**
* 递归调整 (某一个节点至合适的位置)
*
* @param a 数组
* @param i 调整的节点
* @param length 数字大小
*/
private static void adjustDown(int[] a, int i, int length) {
if (i < length) {
int largest = i;
// 左子节点, 右子节点
int left = i * 2 + 1;
int right = i * 2 + 2;
if (left < length && a[left] > a[largest]) {
largest = left;
}
if (right < length && a[right] > a[largest]) {
largest = right;
}
// 如果没有放到合适位置,此时需要交换,继续递归
if (largest != i) {
swap(a, i, largest);
// 调整完,继续向下递归调整, 核心点就是将该值放入子树中合适的位置,可以非递归
adjustDown(a, largest, length);
}
}
}
public static void swap(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
代码中并没有特别复杂,需要注意细节:
- i = len / 2 - 1 只调整非叶子结点
- 只有largest != i 时需要进行递归,说明未到合适的位置