原理
堆的性质:
- 是一棵完全二叉树
- 每个节点的值都大于或等于其子节点的值,为最大堆;反之为最小堆。
堆排序思想:
- 将待排序的序列构造成一个最大堆,此时序列的最大值为根节点
- 依次将根节点与待排序序列的最后一个元素交换
- 再维护从根节点到该元素的前一个节点为最大堆,如此往复,最终得到一个递增序列
性能
时间复杂度为O(NlogN),空间复杂度为O(1),因为利用的排序空间仍然是初始的序列,并未开辟新空间。算法是不稳定的,与初始序列无关。
使用场景
想知道最大值或最小值时,比如优先级队列,作业调度等场景。
算法实现
public class ArrayHeap {
private int[] arr;
public ArrayHeap(int[] arr) {
this.arr = arr;
}
private int getParentIndex(int child) {
return (child - 1) / 2;
}
private int getLeftChildIndex(int parent) {
return 2 * parent + 1;
}
private void swap(int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
/**
* 调整堆。
*/
private void adjustHeap(int i, int len) {
int left, right, j;
left = getLeftChildIndex(i);
while (left <= len) {
right = left + 1;
j = left;
if (j < len && arr[left] < arr[right]) {
j++;
}
if (arr[i] < arr[j]) {
swap(array, i, j);
i = j;
left = getLeftChildIndex(i);
} else {
break; // 停止筛选
}
}
}
/**
* 堆排序。
* */
public void sort() {
int last = arr.length - 1;
// 初始化最大堆
for (int i = getParentIndex(last); i >= 0; --i) {
adjustHeap(i, last);
}
// 堆调整
while (last >= 0) {
swap(0, last--);
adjustHeap(0, last);
}
}
}
稳定性
堆排序存在大量的筛选和移动过程,属于不稳定的排序算法。
适用场景
堆排序在建立堆和调整堆的过程中会产生比较大的开销,在元素少的时候并不适用。但是,在元素比较多的情况下,还是不错的一个选择。尤其是在解决诸如“前n大的数”一类问题时,几乎是首选算法。
近期评论