一、堆的相关概念
1、堆的定义
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为 小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
2、堆的性质
1、堆中某个节点的值总是不大于或不小于其父节点的值。
2、堆总是一棵完全二叉树。
3、堆的存储方式
堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储。
注意: 对于非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低。
4、堆的向下调整
注意: 在调整以parent为根的二叉树时,必须要满足parent的左子树和右子树已经是堆了才可以向下调整。
时间复杂度分析:
最坏的情况即图示的情况,从根一路比较到叶子,比较的次数为完全二叉树的高度,即时间复杂度为O(log2(n))
//以大堆为例
public void adjustDown(int root,int len) {
int parent = root;
int child = 2*parent+1;
while (child < len){
//0、判断是否有左右孩子 有的话 找到最大值 C下标表示最大值下标
if (child+1 < len){
child = this.elem[child] > this.elem[child+1] ? child : child+1;
}
//代码指向到这里,c表示最大值下标
if (this.elem[child] > this.elem[parent]){
//交换
int tmp = this.elem[child];
this.elem[child] = this.elem[parent];
this.elem[parent] = tmp;
parent = child;
child = 2*parent+1;
}else {
break;
}
}
}
5、堆的创建
public void createHeap(int[] array) {
for (int i = 0; i < array.length; i++) {
this.elem[i] = array[i];
this.usedSize++;
}
//i:每棵子树的根节点下标
for (int i = (this.usedSize-1-1)/2; i >= 0 ; i--) {
adjustDown(i,this.usedSize);
}
}
6、堆的插入和删除
插入:
//放入val元素
public void push(int val) {
//0、堆是否是满的--》扩容
if (isFull()){
this.elem = Arrays.copyOf(this.elem,2*this.elem.length);
}
//1、放到数组的最后一个位置
int index = this.usedSize;
this.elem[index] = val;
usedSize++;
//2、进行调整
adjustUp(this.usedSize-1);
}
删除:
public void pop(){
//是否是空的
if (isEmpty()) return;
//最后一个元素和栈顶元素交换
int tmp = this.elem[0];
this.elem[0] = this.elem[usedSize-1];
this.elem[usedSize-1] = tmp;
this.usedSize--;
//调整0号下标的这棵树
adjustDown(0,usedSize-1);
}
7、堆的应用
top-k问题: 找前k个最大的,要建立k个大小的堆。