1.堆的定义
堆是一个可以被看作完全二叉树的数组对象,堆中每个结点的值都不小于或者不大于其左右孩子结点的值。如果父结点的值大于子结点的值,这样的堆称为大顶堆;如果父结点的值小于子结点,则称为小顶堆。(本文以大顶堆为例)
2.堆的存储结构
由于堆是一颗完全二叉树,可以使用数组存储。编号为x的结点,左子结点为2x,右子结点为2x+1。
const int maxn=100;
int heap[maxn];
3.堆的基本操作
(1)建堆
对一个给定的初始序列,要将其建成堆需要把结点从上往下地进行调整,具体步骤:将当前结点与子结点进行比较,如果子结点权值更大,就将当前结点与权值最大的子结点交换,继续让当前结点与子结点比较下去,直到当前结点不存在子结点或者子结点权值都比当前结点要小。
向下调整:
//调整范围为[low,high]
void downAdjust(int low,int high){
int i=low; //当前结点
int j=2*i; //左子结点
while(j<=high){
if(j+1<=high && heap[j+1]>heap[j])
j=j+1;
}
if(heap[j]>heap[i]){
swap(heap[i],heap[j]);
i=j;
j=2*i;
}
else
break;
}
在完全二叉树的结构中,叶子结点的个数为⌈n/2⌉。建堆过程只需要对所有非叶结点倒着枚举进行向下调整即可。倒着枚举是为了保证每个结点都是以其为根结点的子树中权值最大的结点。
void createHeap(){
for(int i=n/2;i>=1;i--){
downAdjust(i,n);
}
}
(2)删除堆顶元素
要想删除堆中值最大的结点后仍然保持堆的结构,只需用最后一个元素覆盖堆顶元素,再对根结点调整即可。
void deleteTop(){
heap[1]=heap[n];
n--;
downAdjust(1,n);
}
(3)插入元素
把要插入的元素放在最后一个结点,向上调整即可。向上调整的操作是:将欲调整结点与父亲结点比较,如果值比父亲结点大则交换两个结点,反复比较直到达到堆顶或者父亲结点的值更大。
//在[low,high]范围内向上调整
void upAdjust(int low,int high){
int i=high;
int j=i/2;
while(j>=low){
if(heap[i]>heap[j]){
swap(heap[i],heap[j]);
i=j;
j=i/2;
}
else
break;
}
}
//插入
void insert(int x){
heap[++n]=x;
upAdjust(1,n);
}
4.堆排序
堆排序是指使用堆结构对一个序列进行排序,以升序排序为例:倒着枚举序列,每次将当前结点heap[i]与堆顶结点heap[1]交换,再在[1,i-1]范围内对堆向下调整,即可实现序列的升序排列。
void heapSort(){
for(int i=n;i>1;i--){
swap(heap[1],heap[i]);
}
downAdjust(1,i-1);
}