1,堆排序基本介绍
- 堆排序是利用堆这种数据结构设计的一种排序算法,类似与选择排序,它的最慢,最好,平均时间复杂度都是
O(nlogn)
,是不稳定排序 - 堆是具有以下性质的完全二叉树:每个节点的值都大于或者等于它的左右子节点的值,称为大顶堆;每个节点的值都小于或者等于左右的值,称为小顶堆;注意此处没有要求左右节点的顺序关系
- 一般升序使用大顶堆,降序使用小顶堆
2,堆排序基本思想
- 首先根据大顶堆的基本格式,将无序数组转换为符合大顶堆规则的数组
- 此处转换先根据算法获取到最后一个非叶子节点
index = arr.length / 2 - 1
,以该节点为起点,向前依次遍历非叶子节点,并与左右子节点进行递归比较,依次保证以该节点为顶节点的自身大顶堆化 - 上一步循环处理完成后,保证整个无序数组转换为大顶堆化的数组
- 数组完成大顶堆化转换完成后,此时顶层节点一定是该部分数组的最大数据,将该数据与处理部分数组的最后一个元素进行替换,即类似选择排序,将最大元素放在数组末尾,并用前部分数组继续进行判断
- 第一次进行最大元素转换后,此时将小元素转换到大顶堆二叉树的顶部, 该元素非最大元素,但除该元素外的其他部分都符合大顶堆规则,此时只需要对该元素下沉到合适位置,并将大元素上浮,上浮到顶层的元素即为剩余数组部分的最大元素
- 重复第二步操作直到整个数组完成排序
3,堆排序图解说明
- 首先,初始化一个数组
{4,6,8,5,9}
,并将其转换为顺序存储二叉树
- 然后,找到它的最后一个叶子节点索引
index = length / 2 - 1 = 4 / 2 - 1 = 1
,并以该索引为数据与它的左右节点进行比较,如果左右节点存在大于它的数据,则下沉交换;此处可以看到,6 < 9
交换位置,此处注意,6到9的位置后,如果还存在子节点,则需要递归处理,紧跟着会看到
- 索引1处理完成后,继续往前找,找到下一个非叶子节点索引0,用值4和值9比较,肯定
9 > 4
,继续替换;此时替换后注意,节点4存在两个子节点5和6,而4小于子节点,不满足大顶堆
- 因为以4为顶点的子树不满足大顶堆,则递归进行处理,让4下沉
- 到此为止,由无序数组转为大顶堆数组已经构建完成,例子简单但可以说明问题
- 现在可以开始进行排序了,构造成大顶堆数据后,root节点即0索引位置数据肯定是最大数据,与选择排序算法基本一致,将该数据与最后一个数据互换位置
- 互换后可以发现,将数组分为了左侧数据和右侧数据两部分,左侧数据为待排数组,右侧数据为有序数组,当左侧数组全部归到右侧后,则整个排序完成,那继续往下走;下一步需要排序的数组,就只需要对左侧数组排序,然后替换底层节点,依次类推
- 交换位置后,最下层的节点4取代了最上层的节点9的位置,此时大顶堆树混乱;但需要注意的是,此时的混乱是在规则基础上的混乱,也就是只存在顶层节点这一个点是混乱,只需要将该点下沉到合适的位置,并在下沉过程中,将较大的值上浮,等有序后,顶层节点依旧为该数据部分的最大值,则再次与原数组的倒数第二个值替换
- 按照此逻辑继续,直到数组有序
4,代码实现
package com.self.datastructure.sort;
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] array = new int[10000000];
for (int i = 0; i < 10000000; i++) {
array[i] = (int) (Math.random() * 8000000);
}
long startTime = System.currentTimeMillis();
heapSort(array);
System.out.println("cast time : " + (System.currentTimeMillis() - startTime));
}
public static void heapSort(int[] array) {
for (int i = array.length / 2 - 1; i >= 0; i--) {
adjustHeap(array, i, array.length);
}
for (int i = array.length - 1; i >= 0; i--) {
int max = array[0];
array[0] = array[i];
array[i] = max;
adjustHeap(array, 0, i);
}
}
public static void adjustHeap(int[] array, int index, int length) {
int temp = array[index];
for (int k = index * 2 + 1; k < length; k = (k * 2 + 1)) {
if (k + 1 < length && array[k] < array[k + 1]) {
k++;
}
if (array[k] > temp) {
array[index] = array[k];
index = k;
} else {
break;
}
}
array[index] = temp;
}
}