优先级队列 (堆)C++实现
优先级队列
优先级队列是一种用来维护由一组数据构成的集合的数据结构,其中每个元素都有一个相关的值称为关键字。借助堆结构可以实现优先级队列。
弹出操作
弹出操作,即取出最大元素操作,取出最大元素后,堆的长度减一,通过堆最后元素与一个元素交换,再调用maxHeapify
函数维护堆的性质,保证弹出堆顶元素后的堆仍然保持堆性质
- C++代码
int PriorityQueue::extractMax() {
if (empty()) {
printf("Heap underflow!\n");
return INT_MIN;
}
int top = heap[1];
heap[1] = heap[size--];
maxHeapify(1);
return top;
}
- 伪代码
HEAP-EXTRACT-MAX(A)
if A.heap-size < 1
error "heap underflow"
max = A[1]
A[1] = A[A.heap-size]
A.heap-size = A.heap-size - 1
MAX-HEAPIFY(A, 1)
return max
维护堆性质
维护堆的性质,对于给定的下标i
,以i
结点为根节点的堆左右子树都是最大堆,此时i
对应结点的关键字可能小于其孩子节点,则违背了堆性质,通过向下调整使得这个堆满足最大堆的性质
堆调整算法选取根节点左右孩子关键字较大值与根节点交换,再递归进入被交换的左右孩子为根节点的堆,继续向下调整,直到当前的堆满足最大堆性质或者调整到叶子节点。
- C++代码
void MaxHeap::maxHeapify(int i) {
int leftChild = left(i);
int rightChild = right(i);
int largest = i;
if (leftChild <= heapSize && sequence[leftChild] > sequence[largest]) {
largest = leftChild;
}
if (rightChild <= heapSize && sequence[rightChild] > sequence[largest]) {
largest = rightChild;
}
if (largest != i) {
// 选取左右孩子结点关键字较大的与根节点交换
swap(sequence[i], sequence[largest]);
// 递归进入被调整的子树对应的堆继续向下调整
maxHeapify(largest);
}
}
- 伪代码
MAX-HEAPIFY(A, i)
l = LEFT(i)
r = RIGHT(i)
if l <= A.heap-size and A[l] > A[i]
largetest = l
else largetest = i
if r <= A.heap-size and A[r] > A[largetest]
largetest = r
if largetest != i
exchange A[i] with A[largetest]
MAX-HEAPIFY(A, largetest)
更新关键字
根据输入的下标和关键字,更新给定下标对应的关键字的值,并维护堆性质。为了维护堆性质,在从当前结点到根结点的路径上,为新增的关键字寻找恰当的插入位置,通过使当前元素不断与其父结点进行比较,若当前元素关键字较大,则与其父结点交换,不断重复,直到当前元素的关键字小于其父结点关键字位置,此时保证堆已重新满足堆的性质
- C++代码
void PriorityQueue::increaseKey(int i, int key) {
if (key < heap[i]) {
// 若当前结点的关键字比给定关键字大
printf("New key is smaller than current key!\n");
return;
}
heap[i] = key;
while (i > 1 && heap[parent(i)] < heap[i]) {
swap(heap[i], heap[parent(i)]);
i = parent(i);
}
}
- 伪代码
HEAP-INCREASE-KEY(A, i, key)
if key < A[i]
error "new key is smaller than current key"
A[i] = key;
while i > 1 and A[PARENT(i)] < A[i]
exchange A[i] with A[PARENT(i)]
i = PARENT(i)
插入操作
插入操作,通过更新下标对应的值的函数来实现,在堆对应数组中增加一个元素,将这个元素设为最小值,堆内元素数量size
增加1
,再更新这个新增加的元素的值为欲插入的值即可
- C++代码
void PriorityQueue::push(int key) {
heap.push_back(INT_MIN);
size++;
increaseKey(size, key);
}
- 伪代码
MAX-HEAP-INSERT(A, key)
A.heap-size = A.heap-size + 1
A[A.heap-size] = -INF
HEAP-INCREASE-KEY(A, A.heap-size, key)
实现代码
/*
author : eclipse
email : eclipsecs@qq.com
time : Wed Jul 15 22:07:49 2020
*/
#include <bits/stdc++.h>
using namespace std;
class PriorityQueue {
private:
int size;
vector<int> heap;
int extractMax();
void maxHeapify(int i);
int parent(int i);
int left(int i);
int right(int i);
void increaseKey(int i, int key);
public:
PriorityQueue();
bool empty();
int pop();
void push(int key);
};
PriorityQueue::PriorityQueue() {
heap.resize(1);
size = 0;
}
int PriorityQueue::parent(int i) {
return i / 2;
}
int PriorityQueue::left(int i) {
return i * 2;
}
int PriorityQueue::right(int i) {
return i * 2 + 1;
}
bool PriorityQueue::empty() {
return size == 0;
}
int PriorityQueue::extractMax() {
if (empty()) {
printf("Heap underflow!\n");
return INT_MIN;
}
int top = heap[1];
heap[1] = heap[size--];
maxHeapify(1);
return top;
}
void PriorityQueue::maxHeapify(int i) {
int leftChild = left(i);
int rightChild = right(i);
int largest = i;
if (leftChild <= size && heap[leftChild] > heap[largest]) {
largest = leftChild;
}
if (rightChild <= size && heap[rightChild] > heap[largest]) {
largest = rightChild;
}
if (largest != i) {
swap(heap[i], heap[largest]);
maxHeapify(largest);
}
}
int PriorityQueue::pop() {
return extractMax();
}
void PriorityQueue::increaseKey(int i, int key) {
if (key < heap[i]) {
printf("New key is smaller than current key!\n");
return;
}
heap[i] = key;
while (i > 1 && heap[parent(i)] < heap[i]) {
swap(heap[i], heap[parent(i)]);
i = parent(i);
}
}
void PriorityQueue::push(int key) {
heap[++size] = INT_MIN;
increaseKey(size, key);
}
int main(int argc, char const *argv[]) {
PriorityQueue *priorityQueue = new PriorityQueue();
vector<int> array;
int N;
scanf("%d", &N);
array.resize(N);
for (vector<int>::iterator it = array.begin(); it != array.end(); it++) {
scanf("%d", it);
}
printf("Heap is %s empty!\n", priorityQueue->empty() ? "" : "not");
for (vector<int>::iterator it = array.begin(); it != array.end(); it++) {
priorityQueue->push(*it);
}
while (!priorityQueue->empty()) {
printf("%d ", priorityQueue->pop());
}
return 0;
}
测试数据
10
1 2 3 4 5 6 7 8 9 10
测试结果
Heap is empty!
10 9 8 7 6 5 4 3 2 1
鸣谢
最后
- 上述伪代码来自算法导论
- 由于博主水平有限,不免有疏漏之处,欢迎读者随时批评指正,以免造成不必要的误解!