堆排序
2025/8/16...大约 2 分钟
堆排序
堆排序是依赖堆的特性完成的排序。
堆分为小顶堆和大顶堆,堆是满二叉树结构且每个节点都是这个节点子树的最大或最小值,这样的结构为堆。根节点大的为大根堆,根节点为小的为小根堆。
堆排序,首先将数组映射成一个堆结构。可以映射成堆结构的原因是:数组的每个位置可以映射到堆的每个节点上。
排序原理:
我们现将数组的每个元素构造成大顶堆,然后逐个将堆顶与堆底交换,然后将堆底从堆中移除,重新维护大顶堆结构。周而复始可以将每个元素排好序。
如下图可以直观的感受数组和堆结构位置的映射关系
可以看到,重点的就是堆结构的维护。
插入:比较当前节点的父节点大小,比父节点大交换,直到不比父节点大或到达堆顶
更新:与两个子节点大的数比较,如果比节点小交换,直到比两个子节点都大或到达叶子节点
代码实现
package learn.note.algorithm.sort.three;
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] list = new int[]{9,28,43,50,76,90,86,94,94,97};
heapSort(list);
// 对数器
CompareMachineUtil.compare(HeapSort::heapSort, null);
Arrays.stream(list).forEach(System.out::println);
}
private static void heapSort(int[] list) {
if (list == null || list.length <= 1) {
return;
}
int heapIndex = 0;
// 构建大顶堆
for (int i = 0; i < list.length; i++) {
heapInsert(list, heapIndex++);
}
// 将最后一个与从大顶堆堆顶交换,断掉最后一个与堆的联系。重新维护大顶堆
while (heapIndex > 0) {
swap(list, --heapIndex, 0);
heapify(list, 0, heapIndex);
}
}
private static void heapify(int[] list, int index, int heapIndex) {
int left = index * 2 + 1;
while (left < heapIndex) {
int largest;
if (left + 1 >= heapIndex) {
// 只有一个孩子
largest = left;
} else {
// 俩孩子比较大小
largest = list[index * 2 + 1] > list[index * 2 + 2] ? index * 2 + 1 : index * 2 + 2;
}
largest = list[largest] > list[index] ? largest : index;
if (largest == index) {
break;
}
swap(list, largest, index);
index = largest;
left = index * 2 + 1;
}
}
private static void heapInsert(int[] list, int heapIndex) {
// 这里要减一
while (list[heapIndex] > list[(heapIndex - 1) / 2]) {
swap(list, heapIndex, (heapIndex - 1) / 2);
heapIndex = (heapIndex - 1) / 2;
}
}
private static void swap(int[] list, int i, int j) {
int temp = list[i];
list[i] = list[j];
list[j] = temp;
}
}