常见的排序算法:堆排序
作者:互联网
文章目录
1. 原理
- 要了解堆排序我们需要先了解堆,堆分为大根堆和小根堆,大根堆就是一颗顺序存储的"二叉树",这棵二叉树的父亲节点的值大于任意孩子节点的值,这样的堆叫做大根堆。
- 那么根据大根堆的性质,我们可以知道,大根堆中第一个元素是这个堆中最大的元素,那么我们每次将无需区间的最后一个元素与第一个元素交换,然后将最后一个元素添加到有序区间,将无需区间重新调整为一个大根堆,通过这样的调整,有序区间中元素个数增加的过程中,数组也趋于有序,这样的排序算法就是堆排序。
- 那么我们可以使一个待排序数组变成一个"大根堆",然后通过上述的方法来进行排序。我们先调整每一个子树,使它成为一个大根堆,最后调整整棵树,这样这个数组就变成了一个大根堆。
- 那么这个调整的过程是怎么样的呢?首先我们从堆可以知道,父亲节点的下标 parent 和左孩子节点的下标关系为 child = 2 * parent + 1,我们可以通过判断父亲节点的大小和左右孩子大小的最大值来判断这个树是不是大根堆,如果左右孩子大小的最大值大于父亲节点的值我们就交换这两个值。可是我们交换了之后以 child 下标节点为父亲节点的树也可能不再是大根堆了呀,那我们让 parent = child, child = 2 * parent + 1,继续调整它的子树不就行了吗。
2. 代码实现
- 话不多说,上代码:
public void heapSort(int[] arr) {
// 将数组调整为一个"大根堆"
// (arr.length - 2) / 2 为堆的最后一个叶子节点的父节点
for (int i = (arr.length - 2) / 2; i >= 0; i--) {
adjustDown(arr, i, arr.length);
}
// 开始调整
// 每次调整由于无需区间交换后的最后一个以及有序了,所以最后一个元素不需要调整
int end = arr.length - 1;
while (end > 0) {
int tmp = arr[end];
arr[end] = arr[0];
arr[0] = tmp;
adjustDown(arr, 0, end);
end--;
}
}
/**
* @param arr 待调整数组
* @param end 标记调整区间的长度
*/
public void adjustDown(int[] arr, int parent, int end) {
int child = 2 * parent + 1;
while (child < end) {
// 保证 child 下标指向的元素是两个子节点中最大的
if (child + 1 < end && arr[child] < arr[child + 1]) {
child++;
}
if (arr[parent] < arr[child]) {
int tmp = arr[parent];
arr[parent] = arr[child];
arr[child] = tmp;
parent = child;
child = 2 * parent + 1;
} else {
// 由于是从最后一个叶子节点开始调整,那么 arr[parent] > arr[child] 时
// 以 arr[parent] 为根节点的这棵树以及是"大根堆"了,不需要继续向下调整了
break;
}
}
}
- 排序的稳定性:存在跳跃式交换,堆排序不稳定。
3. 复杂度
时间复杂度 | 空间复杂度 |
---|---|
O(n*logn) | O(1) |
数据不敏感 | 数据不敏感 |
- 小菜鸡一枚,大家有问题可以评论,大家一起讨论。
标签:大根堆,arr,end,parent,堆排序,算法,child,排序,节点 来源: https://blog.csdn.net/qq_52574924/article/details/119332680