本文介绍一下重要的数据结构:堆,并以C++实现了整数最小堆。
堆是一种完全二叉树,特点是其根大于/小于其儿子,并且这个特性是递归定义的。这样的数据结构用于实现logN复杂度下进行一次最值得查询。
上图显示了一个最小堆。由于堆是完全二叉树,所以底层的数据结构用数组就可以了(当然C++里用vector更好)。那么,我们应该使堆具有哪些操作呢?首先我们应该可以实现对元素的插入。假设我们已经有一个堆,如何插入一个元素且保持对的性质呢?
首先我们写一个Heap类,底层用一个vector保存数据,把data[0]设为哨兵,这样有利于下标的操作。加入的时候,先把数据扔到数组尾部,然后再逐步往上(沿着父节点)直到调整到合适的位置。
上图实现了14的插入,具体代码如下:
void push(int temp)
{
data.push_back(temp);
int cur=data.size()-1;
while(cur!=1)
{
int father=cur/2;
if(data[cur]<data[father])
{
int t=data[cur];
data[cur]=data[father];
data[father]=t;
}
cur=father;
}
}
下面我们考虑如何删除并输出最小值。显而易见,最小值就是data[1]。此时,我们把最尾部的数甩到根上,再逐步往下移动到合适位置。
由上图,我们此时应该对31进行percolate down,具体实现(包括pop)如下:
void pop()
{
cout<<data[1]<<endl;
data[1]=data[data.size()-1];
data.pop_back();
percolateDown(1);
}
void percolateDown(int hole)
{
int child;
int tmp=data[hole];
for(;hole*2<data.size();hole=child)//中间判断是不是叶子,最后更新hole(当然也可以写在循环中)
{
child=hole*2;//left child
if(child!=data.size()-1&&data[child+1]<data[child])//if there is a right child and less then the left one
child++;
if(data[child]<tmp)//less than child,then swap
data[hole]=data[child];
else
break;
}
data[hole]=tmp;//补上hole,此时hole显然已经变了
}
最后,我们还要想办法实现把乱序数组初始化为堆。其实我们只要对数组的前一半用percolate down就好了。
另外,优先队列其实就是堆的一种外包装,pop操作弹出的事最值,这是利用堆来实现的,下面是heap类的完整代码(省去了析构函数),实际上就可以当做优先队列来用了。
class Heap
{
private:
vector<int> data;
public:
Heap(){data.push_back(-1);}
Heap(const vector<int>& item)
{
data.push_back(-1);
for(int i=0;i<item.size();i++)
data.push_back(item[i]);
}
void push(int temp)
{
data.push_back(temp);
int cur=data.size()-1;
while(cur!=1)
{
int father=cur/2;
if(data[cur]<data[father])
{
int t=data[cur];
data[cur]=data[father];
data[father]=t;
}
cur=father;
}
}
void pop()
{
cout<<data[1]<<endl;
data[1]=data[data.size()-1];
data.pop_back();
percolateDown(1);
}
void percolateDown(int hole)
{
int child;
int tmp=data[hole];
for(;hole*2<data.size();hole=child)//中间判断是不是叶子,最后更新hole(当然也可以写在循环中)
{
child=hole*2;//left child
if(child!=data.size()-1&&data[child+1]<data[child])//if there is a right child and less then the left one
child++;
if(data[child]<tmp)//less than child,then swap
data[hole]=data[child];
else
break;
}
data[hole]=tmp;//补上hole,此时hole显然已经变了
}
bool empty()
{
return !(data.size()-1);
}
void print()
{
for(int i=1;i<data.size();i++)
cout<<data[i]<<' ';
cout<<endl;
}
void build_heap()
{
for(int i=(data.size()-1)/2;i>=1;i--)
{
percolateDown(i);
}
}
};