优先级队列的实现,与堆排序的对比(C语言实现)

本文介绍了使用大顶堆实现的优先级队列,它不同于传统队列的“先进先出”原则,而是遵循“优先级最高(或最低)的元素最先出列”。对比堆排序,优先级队列在入列、出列和元素修改时有特定维护堆的方法。文章通过代码展示了C语言实现的优先级队列,并讨论了其在动态查找集合最小值等问题上的应用优势。
摘要由CSDN通过智能技术生成

我们在第四章学习了使用数组实现的队列。这样的队列就是在tail的位置入列 (插入数据),在head的位置出列 (删除数据)。遵循“先进先出”的原则。这里我们要介绍的“优先级队列”是用“堆”实现的。“优先级队列”不再遵循“先进先出”的规则。小顶堆实现的优先级队列遵循“优先级最低的元素最先出列”的原则,大顶堆实现的优先级队列遵循“优先级最高的元素最先出列”的原则。

堆排序与优先级队列的区别:
在这里插入图片描述下面的例子中,入列的元素大小是无序的,实现的是大顶堆。所以这个优先级队列是按照从大到小次序出列的。下面,我们就用代码实现优先级队列:

数组arrResource中存放着入列用的10个元素:

int arrResource[10] = {
   3, 4, 1, 2, 3, 6, 5, 9, 3, 8}; //入列的元素
int priorQueue [10]; //用数组实现堆,用堆实现优先级队列 

int totalNum = 10; //arrResource中一共有10个元素 
int size = 0; //优先级队列中有0个元素 
int dequeuedNum = 0; //已经出列的元素个数

与堆排序相比,这里一共多了四个函数:
1、显示优先级队列中的所有元素。这个函数就是简单的遍历数组。

void showQueue() 
{
   
	for(int i = 0; i < size; i++)
	{
   
		printf("%d, ", priorQueue[i]);
	}
	printf("\r\n");
}

2、优先级队列的入列。数据进入优先级队列之后可能会破坏堆,所以要重新维护堆。如果一共要入列k个元素,则现场维护堆的时间成本是O(K * logN),而完全重建堆的时间复杂度为O(N)。这时会有一个什么时候维护堆的问题。如果只是入列一两个元素,那就应该选择现场维护。但是如果一次性入列很多个元素,那么现场维护的时间成本O(K * logN) 可能高于重新建堆 O(N),所以应该选择完全重建堆。这里,我们选择现场维护的方式。

void enqueue(int n) //把元素n入列 
{
   
	priorQueue[size] = n; //入列 
	size++; //优先级队列中的元素 + 1 
	shiftUp(size - 1); //每次入列都进行维护。注意,这是向上调整
}

3、优先级队列的出列。利用堆的方式出列:只出列堆顶元素,且在出列 (堆中的元素发生变化) 之后重新维护堆,使剩余元素符合大顶堆的要求。这样才能保证每次输出的都是最大值。

int dequeue() //优先级队列出列,得到最大值 
{
   
	if(size <= 0) //防止出错 
	{
   
		return -1; //返回-1表示出错 
	}
	int max = priorQueue[0]; //记录当前堆中的最大值
	
	//这三行就是堆排序中的一次循环
	swap(0, size - 1); //把首元素与尾元素交换
	size--; //堆的大小-1
	shiftDown(0); //重新维护堆:向下调整数据
	dequeuedNum++; //出列的元素个数++
	 
	return max; //返回最大值 
} 

4、优先级队列中的元素的修改。堆排序算法中不修改堆中的元素的数据。但是在优先级队列中可以修改。那么问题来了:对于一个大顶堆,如果修改之后的值比原先大,该怎么调整;如果修改之后的值比原先小,又该怎么调整呢?
把所有数据重建堆 (完整维护堆) 当然是一个可行的方法,但是这样做的时间复杂度为O(N)。由于绝大部分数据都在正确的位置,所以不如做一次部分维护。部分维护的时间复杂度为O(logN)。因为这是一个大顶堆,所以如果修改之后的数比原先大,那么这个数就应该尽量往堆顶方向调整,也就是调用shiftUp() 函数进行部分维护;相反,如果修改之后的数比原先小,就应该调用shiftDown() 函数。

//把堆中priorQueue[index]的值修改成num。修改后要重新维护堆,采用部分维护。 
void editNum(int index, int num)
{
   
	int num0 = pr
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值