堆可以视为一棵完全二叉树,树的每一层都是被填满的,最后一层可能除外,所以堆可以用数组来表示。对于数组中任意位置i上的元素,其左儿子在位置i*2+1,其右儿子在位置i*2+2上,其父节点在位置(i+1)/2-1处。
二叉堆有两种:最大堆和最小堆。最大堆中,除根结点外(其无父结点),每个结点的关键字都不大于其父结点的关键字。最小堆中,除根结点外,每个结点的关键字都不小于其父结点的关键字。
插入元素(insert):
在堆中插入元素X时,在数组的尾部增加一个空穴,如果X可以放在空穴并使堆的性质满足,则插入完成。否则,将空穴的父节点的关键字下移到空穴,使空穴上移。重复该过程直到X可以插入空穴。这种方法叫上滤。下图展示了使用上滤法将14插入一个最小堆的过程(图来自《数据结构与算法分析:C语言描述》)。
删除最小元素(deleteMin):
最小堆的最小元素在根结点处。删除最小元素后,在根结点处产生一个空穴,如果将堆中最后一个元素X放在空穴中可以不破坏堆的性质,则删除完成。否则将空穴两个儿子中较小的一个放入空穴,空穴下移一层。重复该过程直到X可以放入空穴中。这种方法叫做下滤。下图展示了deleteMin的执行过程。
//参考《数据结构与算法分析:C语言描述》
#include <iostream>
#include <vector>
using namespace std;
vector<int> heap;
void insert(int e)
{
heap.push_back(e); //创建空穴
if(heap.size()>1){ //确保不是父结点
int x=heap.size()-1;
int p=heap.size()/2-1;//p是x的父节点的坐标
while((p>=0)&&(e<heap[p])){
heap[x]=heap[p]; //父结点下移至空穴
x=p; p=(p+1)/2-1; //空穴上移
}
heap[x]=e; //插入e
}
}
int deleteMin()
{
int c=0,i=0;
int last=*--heap.end(),min=heap[0];
for( i=0;i*2<heap.size()-1;i=c){
c=i*2+1; //左儿子
if((c!=heap.size()-1)&&(heap[c+1]<heap[c]))
++c;
if(last>heap[c])
heap[i]=heap[c];
else
break;
}
heap[i]=last;
heap.pop_back();
return min;
}
int main()
{
for(int i=0;i<11;++i){
int a; cin>>a;
insert(a);
}
for(int i=0;i<10;++i){
for(auto x:heap)
cout<<x<<" ";
deleteMin();
cout<<endl;
}
}