「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」。
前言
上一篇章我们使用了选择排序这个广为人知的排序算法,他是非常简单的。而本章我们将要给大家讲解另一个比较难懂的排序,堆排序,他主要利用了完全二叉树的特点来实现。
堆排序分析
堆排序的复杂度: 时间复杂度平均为O(nlog₂n) 时间复杂度最坏为O(nlog₂n) 时间复杂度最好为O(nlog₂n) 空间复杂度为O(1) 稳定性为不稳定
由上述时间复杂度来看堆排序的速度优势特别大,这也是得益于他的堆完全二叉树构造,并且空间复杂度也适中,他的所有复杂度综合来看目前属于我们前几期排序的第一名。
堆的排序思路
堆分为两种 大顶堆 和 小顶堆。 大顶堆的特点是每个节点的值都大于或等于其左右孩子节点的值,所以每次从底部开始向上进行对比,然后把大的节点往上移动。 小顶堆的特点是每个节点的值都小于或等于其左右孩子节点的值,然后也开始进行对比,把小的节点往上移动。 先构造一个大顶堆 7 | | 5 4 || || 32 12 上面可以看出虽然这个二叉树中不是有序的,但是他的每个根节点都比下面的节点大,所以这是一个大顶堆,虽然不是有序的但是我们只关注他的顶堆,因为我们后续的排序就是根据顶堆来进行排序。
小顶堆与上面大顶堆异曲同工。
堆的代码实现
堆排序的主要思路:通过大顶堆与小顶堆的构造来实现对无序数列的升序与排序,假设我们现在要降序,那么我们要构造得就是小顶堆,通过每次构造来获得最小的数,把最小数存进一个数组来进行排序。 代码实现:
@Test
public void headSort() {
//构造一个无序堆,后面进行对堆的排序
for (int i = (arr.length) / 2 - 1; i >= 0; i--) {
headAdjust(arr, arr.length, i);
}
//构造最小堆,把最大的放在队尾把最小的放在顶部
for (int i = arr.length - 1; i >= 1; i--) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
//交换堆位置
headAdjust(arr, i, 0);
}
System.out.println(getArr(arr));
}
/**
* 这个方法主要用于堆中进行交换获取顶部最小堆的数
*/
private static void headAdjust(int[] list, int len, int i) {
int k = i, temp = list[i], index = 2 * k + 1;
while (index < len) {
if (index + 1 < len) {
if (list[index] < list[index + 1]) {
index = index + 1;
}
}
if (list[index] > temp) {
list[k] = list[index];
k = index;
index = 2 * k + 1;
} else {
break;
}
}
list[k] = temp;
}