问题描述
优先级队列(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;
}