二叉堆
类似于完全二叉树的结构,使用数组来存储元素。
将二叉树的节点按层级顺序放入数组中。
最大堆:每个元素都大于等于其子节点元素的值。
最小堆:每个元素都小于等于其子节点元素的值。
在一个堆中(从0开始计数),位置为k的节点的父节点的位置为(k-1)/2,两个子节点的位置为2k+1和2k+2。
堆的有序化:当堆的状态被打破时,遍历堆将堆的状态恢复。
以下以最大堆为例:
当某个节点值增大(如:在堆底加入一个新节点),需要由下至上恢复堆(上浮)
private static void swim(int[] a, int k){
int parent = (k - 1)/2;
while (k >= 0 && a[parent] < a[k]){
exch(a, parent, k);
k = parent;
parent = (k - 1) / 2;
}
}
当某个节点值减小(如:根节点替换为较小元素),需要由上至下恢复堆(下沉)
private static void sink(int[] a, int k, int len){
int sub = 2 * k + 1;
while (sub < len){
if ((sub + 1 < len) && (a[sub] < a[sub + 1])) sub++;
if (a[k] >= a[sub]) break;
exch(a, k, sub);
k = sub;
sub = 2 * k + 1;
}
}
堆排序
首先,需要将数据堆化。
然后,不断将堆顶元素交换到当前数组尾,并将剩余元素恢复为堆。
public static void sort(int[] a){
int N = a.length;
//建堆
//方法一:在堆中每增加一个元素就上浮
//for (int i = 1; i < N; i++){
// swim(a, i);
//}
//方法二:除没有子元素的元素外,元素倒序下沉。可减少一半的数据操作。
for (int i = (N - 1)/ 2; i >= 0; i--){
sink(a, i, N);
}
//交互元素到堆尾,堆不断缩小,数组逐渐有序
for (int i = N - 1; i > 0; i--){
exch(a, 0, i);
sink(a, 0, i);
show(a);
}
}
PS:
堆排序可以用来处理TOPn问题,在内存中维护一个大小为n的最小堆,不断加入新数据。
若新数据比堆顶元素小,不考虑。
若比堆顶元素大,则将堆顶元素替换为新元素,进行下沉。