堆排序,顾名思义,就是要用到堆的知识。其基本的原理也就是选择排序,知识不再使用遍历的方式查找无序区间的最大数,而是用堆来来选择无序区间中的最大值。但是需要注意的是,排升序需要用到最大堆,排降序需要用到最小堆。
排序流程图
排序思想
在讲解堆排序思想前,需要先知道一些关于堆排序的知识:
1.堆在逻辑上事一颗完全二叉树。
2.堆在物理上是保存在数组中(按照层序遍历顺序保存)。
3.最大堆:每一个节点的值都大于或等于其两个子节点的值。
4.最大堆的下沉操作:为满足最大堆的性质,需要将指定位置的数在二叉树的逻辑上,向下交换到合适的位置。
5.最大堆的构建:从层序遍历中最后一个非叶子节点开始,每个节点执行下沉操作,则可完成二叉树的构建。
在堆排序中,需要用到以上的有关于堆的知识,此时来讲解堆排序:
1.将无序数组构建为最大堆的形式,此时堆顶的数就是当前无序数组中最大的数。
2.将无序数组的最后一个数和第一个数交换,则最大的数被交换到了无序数组的最后,成为了有序数组的一部分。
3.将交换后的第一个数(也就是刚刚无序数组的最后一个数)进行下沉操作,以继续满足最大堆的性质。
4.重复上述的操作,直到整个数组排序完成。
排序代码
public static void heapSort(int[] arr){
//从最后一个非叶子节点开始下沉操作,构建最大堆
for (int i = (arr.length - 1 - 1) / 2; i >= 0; i--){
siftDown(arr, i, arr.length);
}
//对数组进行交换和下沉操作
for (int i = arr.length - 1; i > 0; i--){
swap(arr, 0, i);
siftDown(arr, 0, i);
}
}
//下沉操作
private static void siftDown(int[] arr, int i, int length) {
//保证此节点有左节点
while (2 * i + 1 < length){
//左节点的索引
int j = (i << 1) + 1;
//判断是否有右节点,并筛选出两节点的最大值所在的索引
if (j + 1 < length && arr[j + 1] > arr[j]){
++j;
}
//若节点的值小于节点的最大值,则结束循环
if (arr[i] > arr[j]){
break;
}
//交换两节点的值,并更改当前节点的索引
swap(arr, i, j);
i = j;
}
}