一、二叉堆简介
二叉堆是完全二元树或者是近似完全二元树,按照数据的排列方式可以分为两种:大根堆和小根堆。
- 大根堆:父节点键值总是大于等于任何一个子节点键值
- 小根堆:父节点键值总是小于等于任何一个子节点键值
性质: - 索引为i的左孩子的索引是 (2*i+1);
- 索引为i的右孩子的索引是 (2*i+2);
- 索引为i的父结点的索引是 int((i-1)/2);
下图分别是大根堆和小根堆:
二、二叉堆的实现
这里用大根堆来说明,小根堆亦可按照同样的方式来实现。由于父节点与子节点有一定的关系所以二叉堆一般都通过数组来实现的。
2.1 基本定义
template <class T>
struct MaxHeap{
private:
T *mHeap; //存放堆的数组
int mCapacity; //堆的容量
int mSize; //目前堆的大小
};
2.2 堆的插入
假设在最大堆[90,80,70,60,40,30,20,10,50]种添加85,需要执行的步骤如下:
steps:
- 将要插入的元素放到数组最后;
- 和它的父节点的键值作比较;
- 若小于等于父节点的键值,则完成插入,否则将键值与父节点的键值交换,同时父节点变成当前节点,跳到step2。
void filterup(int start){
int c=start; //当前位置,即数组末尾位置
int p=(c-1)/2; //当前位置的父节点位置
T temp=mHeap[c]; //当前位置的键值
while(c>0){
if(temp<=mHeap[p])
break;
else{
mHeap[c]=mHeap[p];
c=p;
p=(p-1)/2;
}
}
mHeap[c]=temp;
}
int insert(T data){
if(mSize==mCapacity)//堆满
return;
else{
mHeap[mSize]=data;//放到数组最后
filterup(mSize);
mSize++; //堆的当前大小+1
return 0;
}
}
2.3 堆的删除
假设从最大堆[90,85,70,60,80,30,20,10,50,40]中删除90,需要执行的步骤如下:
steps:
- 首先删除该数据
- 用大根堆中最后一个元素插入到这个’空位’
- 对比当前节点与其子节点键值的大小
- 若大于等于子树键值,则完成删除,否则交换当前节点和较大子树的键值,同时将该子树作为当前节点,跳到step3
void filterdown(int start,int end){
int c=start; //被删当前节点
int l=2*c+1; //左子树
T temp=mHeap[c];//当前位置的键值
while(l<=end){
//判断左右子树谁大
if(l<end&&mHeap[l]<mHeap[l+1])
l++; //右子树大
if(temp>=mHeap[l])
break;
else{
mHeap[c]=mHeap[l];
c=l;
l=2*l+1;
}
}
mHeap[c]=temp;
}
int getIndex(T data){
for(int i=0;i<mSize;i++)
if(data==mHeap[i])
return i;
return;
}
int remove(T data){
int idx;
if(mSize==0) //堆被清零
return;
idx=getIndex(data);
mHeap[idx]=mHeap[--mSize]; //用最后元素填补
filterdown(index,mSize-1);
return 0;
}
三、Acknowledgement
感谢大佬skywang12345。