优先级队列的实现
前言
优先级队列是一种重要的数据结构,对于存储其中的元素,总是选择优先级最高的先出队,在优先队列中,每个元素都有一个与之关联的优先级,高优先级元素会排在队首,低优先级元素排在队尾。
实现优先队列的常用方式是使用堆排序实现。堆可以分为大顶堆和小顶堆,大顶堆的根节点总是大于左右节点,小顶堆的根节点总是小于左右节点。根据这个规则,可以通过不断地插入和删除操作,保证堆的有序性。
一、通过c++中的STL库对优先级队列进行实现
在优先级队列中元素具有优先级,越高优先级的元素越先出队。C++ STL库中已经提供了优先队列的实现。
下面是代码实现,一个是按元素大的优先出队,一个是按照元素小的优先出队
#include <iostream>
#include <queue>
#include <functional>
using namespace std;
int main() {
priority_queue<int, vector<int>, less<int>>pq;
priority_queue<int, vector<int>, greater<int>>pq1;
pq.push(3);
pq.push(1);
pq.push(4);
pq.push(1);
pq1.push(3);
pq1.push(1);
pq1.push(4);
pq1.push(1);
while (!pq.empty()) {
int top = pq.top();
cout << top <<" ";
pq.pop();
}
cout << endl;
while (!pq1.empty()) {
int top1 = pq1.top();
cout << top1 <<" ";
pq1.pop();
}
return 0;
}
这里使用了C++ STL库中的priority_queue实现。在定义priority_queue时,需要指定元素类型、底层容器类型和比较函数类型。使用less比较函数,即最大值优先;如果指定为greater比较函数,则最小值优先。
二、通过堆排序实现优先级队列
优先级队列可以通过堆来实现,堆是一种完全二叉数,可以通过数组来实现堆排序这一过程。
堆有两种操作:
插入(Insert):将元素插入堆的最后一层末尾,然后不断向上调整,保证堆的性质。
取出(Extract):取出堆的根节点,然后将最后一层最右边的节点与根节点交换,再不断向下调整,保证堆的性质。
代码如下:
#include<iostream>
using namespace std;
// 堆结构体
typedef struct {
int* data; // 数据指针
int size; // 堆的大小
int capacity; // 堆的容量
} Heap;
// 初始化堆
void init(Heap* h, int capacity) {
h->data = new int[capacity + 1]; // 堆从1开始存储
h->size = 0;
h->capacity = capacity;
}
// 插入元素
void push(Heap* h, int value) {
if (h->size >= h->capacity) return; // 堆已满,插入失败
int i = ++h->size; // 把新元素放到堆底
while (i > 1 && h->data[i / 2] > value) { // 不断向上调整
h->data[i] = h->data[i / 2];
i /= 2; // i指向它的父节点
}
h->data[i] = value; // 插入新元素
}
// 取出元素
int pop(Heap* h) {
if (h->size == 0) return -1; // 堆为空,取出失败
int value = h->data[1]; // 取出堆顶元素
int last = h->data[h->size--]; // 取出堆底元素,并把堆的大小减1
int i = 1, j = 2; // j指向i的左子节点
while (j <= h->size) {
if (j + 1 <= h->size && h->data[j + 1] < h->data[j]) j++; // 如果右子节点比左子节点小,就选右子节点
if (last > h->data[j]) { // 如果最后一个节点比j节点大,就继续向下调整
h->data[i] = h->data[j];
i = j;
j *= 2;
}
else {
break;
}
}
h->data[i] = last; // 把最后一个节点插入到i节点的位置
return value;
}
int main() {
Heap h;
int value;
init(&h, 1000);
int n;
cin >> n;
for (int i = 0; i <n; i++) {
cin >> value;
push(&h, value);
}
for (int i = 0; i < n; i++) {
cout<< pop(&h)<<" ";
}
return 0;
}
首先通过定义一个堆(Heap)的结构体,其中data是一个指向元素数组的指针,size是堆的大小,capacity是堆的容量。init函数用来初始化堆。push用来插入元素,先判断堆是否已满,然后将新元素插入到堆底,不断向上调整堆,直到插入位置符合堆的性质为止。pop函数用来取出元素,先判断堆是否为空,然后将堆顶元素取出,将堆底元素取出,将堆底元素放到堆顶,不断向下调整堆,直到堆符合性质为止。
先将最小的元素与最后一个元素调整位置,然后出队最小的元素,然后调整堆,不断循环实现堆排序的最小元素优先出队。
时间复杂度和空间复杂度
由于是通过最小堆实现的优先级队列,
时间复杂度:
插入操作(push)的时间复杂度是O(log n),其中n是堆的大小。每次插入操作可能需要进行最多log n次的比较和交换,以维持堆的性质。
删除操作(pop)的时间复杂度也是O(log n),因为每次删除操作同样可能需要进行最多log n次的比较和交换。
总的时间复杂度为O(n*logn)
空间复杂度:
初始化堆的空间复杂度是O(n),因为需要分配一个大小为n+1的数组来存储堆数据。
插入和删除操作的空间复杂度是O(1),因为这些操作只涉及到常数个变量的修改,不需要额外的空间
总结
实现优先级队列可以直接使用c++中的STL库中的优先级队列实现,也可以通过堆排序,可以分成大顶堆和小顶堆,实现优先级高的先出队。