优先级队列 | Priority Queue | C/C++实现

问题描述

优先级队列(Priority Queue)是一种数据结构,其存储的数据集合S中,各个元素均包含键值。优先级队列主要进行下述操作:

insert(S, k):向集合中插入元素k
extractMax(S):从S中删除键值最大的元素并返回该键值

请编写一个程序,对优先级队列S执行insert(S, k)和extractMax(S)。队列元素为整数,键值为其自身。

输入: 对优先级队列S输入多条命令。命令以insert k、extract、end的形式给出,每个命令占1行。这里的k代表插入的整数,end代表命令输入完毕。
输出: 每执行1次extract命令,就输出1个优先级队列S中取出的值,每个值占1行。
限制:
命令数不超过2000000
0 ≤ k ≤ 2000000000

输入示例

insert 8
insert 2
extract
insert 10
extract
insert 11
extract
extract
end

输出示例

8
10
11
2

讲解

优先最大键值的队列称为最大优先级队列,可以通过最大堆实现。这里我们用数组A实现大小为H的二叉堆。

insert(key)用来向最大优先级队列S中添加key,其算法如下:

insert(key)
	H++
	A[H] = -INFTY
	heapIncreaseKey(A, H, key)//在A[H]中设置key

heapIncreaseKey(A, i, key)操作用来增加二叉堆元素 i 的键值,其算法如下:

heapIncreaseKey(A, i, key)
	if key < A[i]
		报错:新键值小于当前键值
	A[i] = key
	while i > 1 && A[parent(i)] < A[i]
		交换A[i]和A[parent(i)]
		i = parent(i)

首先,为保证只有在新键值大于等于当前键值时才变更堆,我们要先检查已有键值,然后再更新键值A[i]。由于A[i]增加后可能会破坏最大堆的性质,因此要向根的方向移动更新后的键值,将其放在恰当位置。只需将当前元素与其父结点进行比较,如果当前元素的值更大则交换这两个元素,然后递归调用即可。

最大优先级队列S中的最大值可由二叉堆的根结点获取。从最大优先级队列S中删除并取出最大元素的算法如下:

heapExtractMax(A)
	if H < 1
		报错:堆向下溢出
	max = A[1]
	A[1] = A[H]
	H--
	maxHeapify(A, 1)

	return max

首先,我们将二叉堆根结点的值(最大值)储存在临时变量max中。接下来将最末尾的值移动至根,堆大小H减1。更新后的根有可能破坏最大堆的性质,所以要从根开始执行maxHeapify。最后,返回之前储存好的max。

AC代码如下

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX 2000000
#define INFTY (1<<30)

int H, A[MAX + 1];

void maxHeapify(int i){
	int l, r, largest;
	l = 2 * i;
	r = 2 * i + 1;
	
	//从左子结点、自身、右子结点中选出最大的结点
	if(l <= H && A[l] > A[i]) largest = l;
	else largest = i;
	if(r <= H && A[r] > A[largest]) largest = r;
	
	if(largest != i){
		swap(A[i], A[largest]);
		maxHeapify(largest);
	} 
}

int extract(){
	int maxv;
	if(H < 1) return -INFTY;
	maxv = A[1];
	A[1] = A[H--];
	maxHeapify(1);
	return maxv;
}

void increaseKey(int i, int key){
	if(key < A[i]) return;
	A[i] = key;
	while(i > 1 && A[i/2] < A[i]){
		swap(A[i], A[i/2]);
		i = i/2;
	}
}

void insert(int key){
	H++;
	A[H] = -INFTY;
	increaseKey(H, key);
}

int main(){
	int key;
	char com[10];
	
	while(1){
		scanf("%s", com);
		if(com[0] == 'e' && com[1] == 'n') break;
		if(com[0] == 'i'){
			scanf("%d", &key);
			insert(key);
		} else {
			printf("%d\n", extract());
		}
	}
	
	return 0;
}
  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值