排序8-堆排序
程序员文章站
2022-03-18 09:40:42
...
利用堆这种数据结构来进行排序的算法, 堆是一种优先队列,它有两种实现,最大堆和最小堆.
堆(以下全都默认为最大堆)看成一棵完全二叉树,但是位于堆顶的元素总是整棵树的最大值,每个子节点的值都比父节点小,由于堆要时刻保持这样的规则特性,所以一旦堆里面的数据发生变化,我们必须对堆重新进行一次构建。
既然堆顶元素永远都是整棵树中的最大值,那么我们将数据构建成堆后,第一次取的元素,就是最大值?取完后把堆重新构建一下,然后再取堆顶的元素,取的就是第二大的值, 反复的取,取出来的数据也就是有序的数据。
1. 示意图:
以[8,2,5,9,7,3]这组数据来演示.
首先,将数组构建成堆。
接下来,我们取出堆顶的数据,也就是数组第一个数,9,取法是将数组的第一位和最后一位调换,然后将数组的待排序范围-1,
现在的待排序数据是[3,8,5,2,7],我们继续将待排序数据构建成堆。
取出堆顶数据,这次就是第一位和倒数第二位交换了,因为待排序的边界已经减1。
继续构建堆
从堆顶取出来的数据最终形成一个有序列表.
2. 流程描述:
- 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
- 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
- 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
3. java代码实现.
/**
* 堆排序
* @param arr 排序数组
*/
public static void sort(int[] arr) {
int length = arr.length;
//构建堆
buildHeap(arr, length);
for (int i = length - 1; i > 0; i--) {
//将堆顶元素与末位元素调换
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
//数组长度-1 隐藏堆尾元素
length--;
//将堆顶元素下沉 目的是将最大的元素浮到堆顶来
sink(arr, 0, length);
}
}
/**
* 构建堆
* @param arr 数组
* @param length 数组范围
*/
private static void buildHeap(int[] arr, int length) {
for (int i = length / 2; i >= 0; i--) {
sink(arr, i, length);
}
}
/**
* 下沉调整
* @param arr 数组
* @param index 调整位置
* @param length 数组范围
*/
private static void sink(int[] arr, int index, int length) {
int leftChild = 2 * index + 1;//左子节点下标
int rightChild = 2 * index + 2;//右子节点下标
int present = index;//要调整的节点下标
//下沉左边
if (leftChild < length && arr[leftChild] > arr[present]) {
present = leftChild;
}
//下沉右边
if (rightChild < length && arr[rightChild] > arr[present]) {
present = rightChild;
}
//如果下标不相等 证明调换过了
if (present != index) {
//交换值
int temp = arr[index];
arr[index] = arr[present];
arr[present] = temp;
//继续下沉
sink(arr, present, length);
}
}
3. 分析: 堆排序的时间复杂度是O(nlogn)。
上一篇: 【数据结构与算法】—— 选择排序