这里以删除最小元素为例。最小-最大堆的根节点元素为最小,则把根节点删除,并调整好堆,使其成为新的最小-最大堆。
一般情况下,把最后一个节点关键值为item重新插入到根节点为空的最小-最大堆中,分以下两种情况讨论:
根节点没有儿子。此时,可把item直接插入根节点。
根节点至少有一个儿子节点。此时,最小-最大堆的关键字值最小节点(不包括item)一定位于根节点的儿子节点或后代节点。设最小关键字值节点的下标为k,则需讨论以下几种情况:
a)item<=heap[k]时,此时由于堆中不存在比item更小的关键字值,则可把item直接插入到根节点。
b)item>heap[k]且k是根节点的儿子节点,此时,由于k是最大节点,所以其后代节点中不可能存在关键字值大于heap[k]的节点,从而也没有关键字值大于item的节点,所以把关键字值heap[k]移到根节点,并将item插入k节点中。
c)item>heap[k]且k是根节点的后代节点,此时,可以把关键字值heap[k]移到根节点,并将item插入k节点中,然后考察k节点的父亲节点parent:如果item>heap[parent],则将item与heap[parent]交换,此时,问题转化为将item插入到以k为根节点的子堆中,而且此子堆的根节点heap[k]已经腾空,这时将item插入到以1为根节点的最小-最大堆而根节点1为空的情况相似。
例子:在此最小-最大堆中item=12,且根节点的所有儿子和后代节点的最小关键字值为9,设关键字值为9节点的下标为k,其父节点下标为parent。因为9<12;且k节点是根节点的后代节点,则属于情况2(c),此时把heap[k]=9移到根节点,由于item=12
/*最小最大堆删除操作,并返回最小元素*/
int delete_min_heap(int *heap,int *n)
{
if(!(*n))
{
printf("The heap is empty.\n");
exit(1);
}
int item,temp;
int k,parent;
int i,last;
int x;
x=heap[1];//根节点关键字值赋给x
item=heap[(*n)--];//最后节点的值赋给item
i=1;
last=(*n)/2;//为重新插入item初始化
/*寻找插入item的位置*/
while(i<=last)//有儿子或后代节点,情况2
{
k=min_child_grandchild(i,*n);//返回最小元素的节点下标
if(item<=heap[k])break;//如情况2(a),直接插入到根节点
else
{
heap[i]=heap[k];//把关键字值移到根节点
if(k<=2*i+1)//k是i的直接儿子,情况2(b)
{
i=k;//把item插入到heap[k]
break;
}
else//k是i的后代节点,情况2(c)
{
parent=k/2;
if(item>heap[parent])
SWAP(item,heap[parent],temp);
i=k;
}
}
}
heap[i]=item;
return x;
}
五、程序
函数声明:
#ifndef FUNCTION_H_INCLUDED
#define FUNCTION_H_INCLUDED
#define SWAP(x,y,t) ((t)=(x),(x)=(y),(y)=(t))
#define MAX_SIZE 10
int heap[MAX_SIZE];
void verify_max(int *heap,int i,int item);
void verify_min(int *heap,int i,int item);
int level(int node);//判断结点属于最大层还是最小层
void min_max_insert(int *heap,int *n,int item);
int delete_min_heap(int *heap,int *n);
int min_child_grandchild(int i,int n);
#endif // FUNCTION_H_INCLUDED
函数定义:
#include
#include
#include
#include "function.h"
/**从最大结点i开始,沿着从i到根结点路径,依次检查最大结点**/
/**查找插入数据item的正确结点,这个结点应有以下性质:从该结点
向上到根的路径上所有最大结点的关键字值都不小于item,而且从结点
i向下到该结点的路径上所有最大关键字值都小于item.**/
/**查找过程中,关键字值小于item的最大结点被下移到下一个最大层**/
void verify_max(int *heap,int i,int item)
{
int grandparent=i/4;//第一个非叶子结点
while(grandparent)
{
if(item<=heap[grandparent])
break;
else
{
heap[i]=heap[grandparent];
i=grandparent;
grandparent/=4;
}
}
heap[i]=item;
}
/**从最小结点i开始,沿着从i到根结点路径,依次检查最小结点**/
/**查找插入数据item的正确结点,这个结点应有以下性质:从该结点
向上到根的路径上所有最小结点的关键字值都不大于item,而且从结点
i向下到该结点的路径上所有最小关键字值都大于item.**/
/**查找过程中,关键字值大于item的最小结点被下移到下一个最小层**/
void verify_min(int *heap,int i,int item)
{
int grandparent=i/4;
while(grandparent)
{
if(item>=heap[grandparent])
break;
else
{
heap[i]=heap[grandparent];
i=grandparent;
grandparent/=4;
}
}
heap[i]=item;
}
int level(int node)//判断结点属于最大层还是最小层
{
double k=log(node)/log(2);
if((int)k%2==0)return 0;//最小层返回0
else return 1;//最大层返回1
}
/**最小-最大堆插入操作**/
void min_max_insert(int *heap,int *n,int item)
{
int parent;
int i;
i=(*n);
i++;
if(MAX_SIZE==i)//判断是否为满堆
{
printf("it is full.\n");
exit(1);
}
parent=(i)/2;//第一个非叶子结点,即结点i的父结点
if(!parent)//判断是否为空堆
heap[1]=item;//序号从1开始,而不是0
else
{
int k;
k=level(parent);//判断parent结点在最大层还是最小层
switch(k)//k=0为最小层,1为最大层
{
case 0:
if(item
{
heap[i]=heap[parent];//交换
verify_min(heap,parent,item);//插入在parent结点向上到根
//的最小层中
}
else
verify_max(heap,i,item);
break;
case 1:
if(item>heap[parent])
{
heap[i]=heap[parent];
verify_max(heap,parent,item);
}
else
verify_min(heap,i,item);
break;
}
}
*n=i;
}
/*确定节点i所有儿子和后代节点的最小关键字值*/
int min_child_grandchild(int i,int n)
{
int child,temp,t;
int j;
child=2*i;//i的儿子节点
temp=heap[child];//保存当前值
while(child<=n)//节点下标小于节点数
{
for(j=child;j<=n;j++)//遍历所有儿子节点和后代节点
{
if(temp>heap[j+1])
{
temp=heap[j+1];//若当前节点的儿子节点或后代节点存在小于当前关键字值,把该节点赋给当前节点
t=j+1;//保存最小节点的下标
}
}
child=j;
}
return t;
}
/*最小最大堆删除操作,并返回最小元素*/
int delete_min_heap(int *heap,int *n)
{
if(!(*n))
{
printf("The heap is empty.\n");
exit(1);
}
int item,temp;
int k,parent;
int i,last;
int x;
x=heap[1];//根节点关键字值赋给x
item=heap[(*n)--];//最后节点的值赋给item
i=1;
last=(*n)/2;//为重新插入item初始化
/*寻找插入item的位置*/
while(i<=last)//有儿子或后代节点,情况2
{
k=min_child_grandchild(i,*n);//返回最小元素的节点下标
if(item<=heap[k])break;//如情况2(a),直接插入到根节点
else
{
heap[i]=heap[k];//把关键字值移到根节点
if(k<=2*i+1)//k是i的直接儿子,情况2(b)
{
i=k;//把item插入到heap[k]
break;
}
else//k是i的后代节点,情况2(c)
{
parent=k/2;
if(item>heap[parent])
SWAP(item,heap[parent],temp);
i=k;
}
}
}
heap[i]=item;
return x;
}
测试程序:
#include
#include
#include"function.h"
int main()
{
int i,item;
int n=0;
for(item=1;item<10;item++)
min_max_insert(heap,&n,item);//插入节点
for(i=1;i<10;i++)
printf("%d ",heap[i]);
printf("\n");
item=delete_min_heap(heap,&n);//删除节点,并返回最小元素
printf("The deleted data is %d\n",item);
for(i=1;i<=n;i++)
printf("%d ",heap[i]);
return 0;
}