一.堆的概念:
堆通常可以被看做一棵树,它满足下列性质:
1.堆中任意节点的值总是不大于(不小于)其子节点的值;
2.堆是一棵完全树。
将任意节点不大于其子节点的堆叫做最小堆或小根堆,而将任意节点不小于其子节点的堆叫做最大堆或大根堆。
常见的堆有二叉堆、左倾堆、斜堆、二项堆、斐波那契堆等等。
二叉堆是完全二叉树,它分为两种: 最大堆和最小堆。
最大堆:父结点的键值总是大于或等于任何一个子节点的键值。
最小堆: 父结点的键值总是小于或等于任何一个子节点的键值。
二.二叉堆直观图:
三.二叉堆的存储方式:
二叉堆一般是通过“数组”实现的。“二叉堆的第一个元素”放在数组索引为0的位置,则父节点与子节点的关系如下:
索引为i的左孩子的索引为:(2*i+1);((i<<1)|1)
索引为i的右孩子的索引为:(2*i+2);((i<<1)+2)
索引为i的父节点的索引为:int((i-1)/2);( floor( (i-1)/2 ) )
但一般我们令第一个元素放在数组索引为1的位置,这样会更方便。
索引为i的左孩子的索引为:(2*i); (i<<1)
索引为i的右孩子的索引为:(2*i+1);((i<<1)|1)
索引为i的父节点的索引为:int(i/2); (i>>1)( floor( i/2 ) )
四.二叉堆插入数据
以最大堆为例:
总结:
1.把要插入的的元素放在末尾。
2.与其父节点比较,若大于父节点,swap即可,直到父节点<1,此时再无父节点可换。
五.删除数据
还是以最大堆为例:
一般我们删除最大值或最小值,即根的元素。
总结:
1.根元素和末尾元素swap,再top--进行删除。
2. 父节点与两个子节点比较,令maxn=max(左孩子,右孩子)。若父节点<maxn,说明父节点没有能力守住这个位置,则swap(maxn,父节点)。直到子节点>top,即再无子节点可换,已经为最小子节点了。
六.大根堆代码实现插入与删除
功能:先输入一个数n,接着输入n个数,表示插入二叉堆中,最后输入一个数m,表示删除m个堆首。
样例输入:5
9 2 4 5 21
1
样例输出:21 9 4 2 5
9 5 4 2
//大根堆
#include<bits/stdc++.h>
#define maxn 10001
using namespace std;
int tree[maxn<<1]; //左右孩子,数组要开2倍
int n;
int top=0; //最大节点下标
void push(int x){
tree[++top]=x;
int k=top;
while((k>>1)>0 && tree[k]>tree[k>>1]){
swap(tree[k],tree[k>>1]);
k>>=1;
}
}
void pop(){
swap(tree[1],tree[top]);
top--;
int k=1;
while(1){
int t=k<<1;
if(tree[t]<tree[t+1]) t++;//左孩子变右孩子
if(t>top) break;
if(tree[k]<tree[t]) swap(tree[k],tree[t]);
else break;
k=t;
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int num;
cin>>num;
push(num);
}
for(int i=1;i<=top;i++) cout<<tree[i]<<" ";
cout<<endl;
//二叉堆删除,删堆首;
int m;
cin>>m;
while(m--){
pop();
}
for(int i=1;i<=top;i++) cout<<tree[i]<<" ";
}
七.小根堆:
同理,与大根堆相反。