什么是堆
堆的物理结构相当于完全二叉树,并且完全二叉树是可以用数组存储的,堆顶元素是数组的第一项。
什么是堆排序
堆排序即将一个乱序的数组组成的二叉树经过调整成为有序数组。
插入一个元素需要将新元素追加到二叉树尾部,然后比较进行调整得到完全二叉树。时间复杂度为O(logN)。
删除堆顶元素,堆顶元素移出去之后将尾部的节点升为堆顶元素,然后进行比较下沉得到最新的堆。
那如何删除堆中指定元素呢?
看了一下知乎:在确定位置之后把堆末尾元素和它交换然后和删除堆顶元素一样不断做下移操作就好。但是需要维护一个hash表来索引每个元素在堆中的位置,保证复杂度O(log n)。
大顶堆和小顶堆
PriorityQueue pq = new PriorityQueue<>(list.length, (a, b) -> (a.val - b.val));
这个是自定义的对象之间的比较,两个对象之间比较大小使用.val数值,然后减号就是说,在维护堆的时候,要判断大小,判断的方法就是两个数相减,然后看大于0还是小于0
,然后减号就是说,在维护堆的时候,要判断大小,判断的方法就是两个数相减,然后看大于0还是小于0。此为构建小顶堆。
面试题:上万级数据取前K个大的数据
常见做法:构造K大的小顶堆,进行遍历比较,下边的代码是根据左程云老师的课写的
public static class TopN{
private int parent(int n){
return (n - 1)/2;
}
private int left(int n){
return 2*n + 1;
}
private int right(int n){
return 2*n + 2;
}
//构建小顶堆
private void buildHeap(int n, int[] data){
for (int i = 1; i < n; i++){
int t = i;
while (t != 0 && data[parent(t)] > data[t]){
int temp = data[t];
data[t] = data[parent(t)];
data[parent(t)] = temp;
t = parent(t);
}
}
}
//调整data[i]
private void adjust(int i, int n, int[] data){
//why <=
if (data[i] <= data[0]){
return;
}
//置换堆顶
int temp = data[i];
data[i] = data[0];
data[0] = temp;
int t = 0;
while (left(t) < n && data[t] > data[left(t)] || right(t) < n && data[t] > data[right(t)]){
if (right(t) < n && data[right(t)] < data[left(t)]){
temp = data[t];
data[t] = data[right(t)];
data[right(t)] = temp;
t = right(t);
}else {
temp = data[t];
data[t] = data[left(t)];
data[left(t)] = temp;
t = left(t);
}
}
}
public void findTopN(int n, int[] data){
buildHeap(n, data);
for (int i = 0; i < n; i++){
adjust(i, n, data);
}
}
}