优先队列 堆

1、模型。

优先队列和(队列与二叉树)有着相似的模型。通过对比二者可以更好的了解优先队列的模型。

优先队列队列二叉树
InsertEnqueueInsert
DeleteMin

Dequeue

 

Delete

 

static int Succ(int Value, Queue Q)
{
	if (++Value == Q->Capacity)Value = 0;
	return Value;
}

void Enqueue(ElementType X, Queue Q)
{
	if (IsFull(Q))FatalError("Full Queue");
	else
	{
		Q->Size++;
		Q->Rear = Succ(Q->Rear, Q);
		Q->Array[Q->Rear] = X;
	}
}

ElementType FrontAndDeQueue(Queue Q)
{
	if (IsEmpty(Q))FatalError("Empty Queue.");
	else
	{
		Q->Size--;
		return Q->Array[Q->Front++];
	}
}

以上的代码是队列中的入队和出队的函数的实现。

 

2、二叉堆的实现

二叉堆(Binary heap)是优先队列的实现中类似二叉树的地方。其具有结构性和堆序性。

   1 )结构性。

堆是一棵完全充满的二叉树(complete binary tree),底层的元素若有空位,则从左向右插入。应当留意的是,这种数据结构的实现可以通过数组实现,同时正好填满整个数组。因此就避免了结构体和指针。

                                                                                 1                                                                                                                                                                                                      /        \                                                                                                                                                                                                2          3                                                                                                                                                                                            /   \        /   \                                                                                                                                                                                        4     5     6    7

以上的图中的数字表示的是数组的下标。可以看到,对于位置为 i 的元素,左儿子在位置 2i 上, 右儿子在 位置 2i + 1上,其父亲在位置⌊ i ⌋上。注意符号⌊⌋是向下取整的意思。

 

 

2)堆序性:对节点 X,X的关键字小于它的儿子,同时大于它的父亲。

      代码表述为Elements[ Parent ]<Elements[ i ] <Elements[ Child ]

 

则根据这两个性质 ,可以实现对优先队列的基本操作。

#include<stdio.h>
#include<stdlib.h>
#define MinPQSize 5
typedef int ElementType;

struct HeapStruct {
	int Capacity;      //能存储的元素个数上限
	int Size;          //当前已有的元素个数
	ElementType* Elements;
};
typedef struct HeapStruct* PriorityQueue;

void FatalError(const char* s)
{
	printf("%s", s);
}

int IsFull(PriorityQueue H)
{
	return H->Size == H->Capacity;
}

int IsEmpty(PriorityQueue H)
{
	return H->Size == 0;
}
PriorityQueue Initialize(int MaxElements)
{
	PriorityQueue H;
	if (MaxElements < MinPQSize)FatalError("Priority queue Size too small.");

	H = (PriorityQueue)malloc(sizeof(struct HeapStruct));
	if (H = NULL)FatalError("Out of Space!");
	H->Elements = (ElementType*)malloc(sizeof(ElementType) * (MaxElements + 1));
	if (H->Elements == NULL)FatalError("Out of Space!");;

	H->Capacity = MaxElements;
	H->Size = 0;
	H->Elements[0] = 0;
}

void Insert(ElementType X, PriorityQueue H)
{
	int i;
	if (IsFull(H))
	{
		FatalError("Priority Queue is full.");
		return;
	}
	for (i = ++H->Size; H->Elements[i / 2] > X; i /= 2)
		H->Elements[i] = H->Elements[i / 2];
/* 注意,假设本该插入到Elements[++H->Size]处的空穴,但是由于我们不能确定插入之后是否满足性质
Elements[(++H->Size)/2]<Elements[H->Size](注:此处在前方已经++H->Size,所以后面不用,也不能
再++了。)因此当其父节点的元素大于 X 时,将其放到其子节点处的空穴,同时在该处产生空穴。并再次循环

比较。其实我认为此处应该先将H->Elements[++H->Size]处的值赋为 X,并将其用来代替 X ,这样更容易理
解。(虽然这样在每次循环的过程中都需要赋值一次了)
*/
	H->Elements[i] = X;
}

ElementType DeleteMin(PriorityQueue H)
{
//删除最小的节点就意味着删除根节点,由于父节点小于子节点的性质。
	int i, Child;
	ElementType MinElement, LastElement;

	if (IsEmpty(H))
	{
		FatalError("PriorityQueue is empty.");
		return H->Elements[0];
	}

	MinElement = H->Elements[1];
	LastElement = H->Elements[H->Size--];
//注意,这里先将LastElement赋值为H->Elements[H->Size],再将Size--。
	for (i = 1; i * 2 <= H->Size; i = Child)
	{
		Child = i * 2;
		if (Child != H->Size && H->Elements[Child + 1] > H->Elements[Child])Child++;
		//这一行是为了找到较大的儿子
		if (LastElement > H->Elements[Child])H->Elements[i] = H->Elements[Child];
		else                                 break;
	}
// 这个循环类似Insert里面的循环操作。只不过是将X改为了LasElement
	H->Elements[i] = LastElement;
	return MinElement;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值