优先队列(Priority Queue):特殊的“队列”,取出元素的顺序是 依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。我们可以利用完全二叉树来表示优先队列,也就是堆
结构性:用数组表示的完全二叉树;
有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)
类型名称:最大堆(MaxHeap)
数据对象集:完全二叉树,每个结点的元素值不小于其子结点的元素值
操作集:最大堆H 属于 MaxHeap,元素item 属于 ElementType,主要操作有:
MaxHeap Create( int MaxSize ):创建一个空的最大堆。
Boolean IsFull( MaxHeap H ):判断最大堆H是否已满。
Insert( MaxHeap H, ElementType item ):将元素item插入最大堆H。
Boolean IsEmpty( MaxHeap H ):判断最大堆H是否为空。
ElementType DeleteMax( MaxHeap H ):返回H中最大元素(高优先级)。
//最大堆的创建
typedef struct HeapStruct *MaxHeap;
struct HeapStruct {
ElementType *Elements; /* 存储堆元素的数组 */
int Size; /* 堆的当前元素个数 */
int Capacity; /* 堆的最大容量 */
};
MaxHeap Create( int MaxSize )
{ /* 创建容量为MaxSize的空的最大堆 */
MaxHeap H = malloc( sizeof( struct HeapStruct ) );
H->Elements = malloc( (MaxSize+1) * sizeof(ElementType));
//Maxsize+1的原因是堆在第0项不放东西,我们一般存放一个哨兵节点
//在最大堆中,哨兵节点存放一个比所有值都大的数字,
//这样可以免去在插入过程中判断i是否 = 1
H->Size = 0;
H->Capacity = MaxSize;
H->Elements[0] = MaxData;
/* 定义“哨兵”为大于堆中所有可能元素的值,便于以后更快操作 */
//在最小堆中,就创建小于所有值的值
return H;
}
由于之前的哨兵节点存在,我们不需要判断i是否等于1,因为i = 1时,比所有的节点都大,应该插在树根上,而这个时候进i/2= 0,已经在堆的外面了,所以我们设置一个哨兵节点。
void Insert( MaxHeap H, ElementType item )
{ /* 将元素item 插入最大堆H,其中H->Elements[0]已经定义为哨兵 */
int i;
//先判别堆有没有满
if ( IsFull(H) ) {
printf("最大堆已满");
return;
}
//i代表了我要插入节点的位置,是H->size+1
i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */
//i/2是父节点的位置,要循环判断是否比父节点大
for ( ; H->Elements[i/2] < item; i/=2 ){//如果比父节点大,父节点就下来
H->Elements[i] = H->Elements[i/2]; /* 向下过滤结点 */
}
//由于之前的哨兵节点存在,我们不需要判断i是否等于1,因为i = 1时,比所有的节点都大,应该插在树根上,而这个时候进i/2= 0,已经在堆的外面了,所以我们设置一个哨兵节点
H->Elements[i] = item; /* 将item 插入 */
}
目标:取出根节点的元素,同时删除一个节点
ElementType DeleteMax( MaxHeap H )
{ /* 从最大堆H中取出键值为最大的元素,并删除一个结点 */
int Parent, Child;
ElementType MaxItem, temp;
//判断空不空
if ( IsEmpty(H) ) {
printf("最大堆已为空");
return;
}
MaxItem = H->Elements[1]; /* 取出根结点最大值 */
/* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */
temp = H->Elements[H->Size--];
//给temp找位置,parent来指示这个位置
for( Parent=1; Parent*2<=H->Size;/*判别是否有左儿子*/ Parent=Child ) {
//从左右儿子中找大的去和paraent比较
Child = Parent * 2;
//先假定做儿子大,
//Parent * 2是左儿子
//Child+1是右儿子
if( (Child!= H->Size) &&(H->Elements[Child] < H->Elements[Child+1]) )
//如果有右儿子,且右儿子比左儿子大
Child++; /* Child指向左右子结点的较大者 */
if( temp >= H->Elements[Child] )
break;
//比儿子大,跳出
else /* 移动temp元素到下一层 */
//左右儿子大的那个比我大,要上调
H->Elements[Parent] = H->Elements[Child];
}
//temp位置找到,赋值
H->Elements[Parent] = temp;
return MaxItem;
从后往前进行建立
//其实是之前删除的思路
void PercDown(MaxHeap H,int p)
{
int Parent,Child;
ElementType temp;
temp = H->Elements[p];//取出根节点的位置
for(Parent = p;Parent*2<=H->Size;Parent = Child){
Child = Parent * 2;//先假设左儿子大,Parent * 2是左儿子(完全二叉树)
if((Child!=H->Size)&&(H->Elements[Child]<H->Elements[Child+1]))
Child++;//如果……右儿子大
if(X>= H->Elements[Child])
break;//如果节点比最大的儿子大
else
H->Elements[Parent] = H->Elements[Child];//如果不是,纳闷替换位置
}
H->Elements[Parent] = temp;//赋值
}
//调整H->Data[]中的元素,使得其满足最大堆的有序性
//假设H->Size个元素已经存在H->Data[]中
void BulidHeap(MaxHeap H)
{
int i;
//从最后一个节点的父节点开始,到根节点1
for(i = H->Size/2;i>0;i--)
PercDown(H,i);
}
原文:https://www.cnblogs.com/lingxueqian/p/13365071.html