许多应用程序都需要处理有序元素,但是不一定要求他们完全有序。很多情况下只需要处理当前最大元素,然后数据集合变化后再继续处理最大元素。在这种情况下,一个合适的数据结构应支持两种关键操作:1删除最大元素2插入元素。优先队列具备这种特质。优先队列可以使元素按照自定义的一个优先级来排序,并非加入顺序来排序,高效实现优先队列具有一定挑战性。
《算法》一书中介绍了维护一个二叉堆来实现高效的优先队列插入删除操作,以下是用C++编写的基于堆的优先队列:
.h 头文件
#include <vector>
#include <iostream>
using namespace std;
/*定义大顶堆模板类*/
template<typename T>
class MaxPQ
{
public:
MaxPQ(int maxN);
~MaxPQ();
/*基本接口*/
bool isEmpty();
void insert(T val);
T delMax();
void swim(int k);
void sink(int k);
/*内部调用的工具接口*/
friend void exchan(vector<T> &a, int j, int k);
friend bool pqless(T & a, int x, int y);
private:
vector<T> pq;
int N = 0;
};
/*定义小顶堆模板类*/
template<typename T>
class MinPQ
{
public:
MinPQ(int maxN);
~MinPQ();
bool isEmpty();
void insert(T val);
T delMin();
void swim(int k);
void sink(int k);
friend void exchan(vector<T> &a, int j, int k);
friend bool pqless(T & a, int x, int y);
private:
vector<T> pq;
int N = 0;
};
/*易位操作*/
template<typename T>
inline void exchan(vector<T>& a, int j, int k)
{
T tmp;
tmp = a[j];
a[j] = a[k];
a[k] = tmp;
}
/*大小比较,该函数仍然使用字典顺序来比较队列元素*/
template<typename T>
inline bool pqless(T & a,int x, int y)
{
if (a[x] <= a[y])
return true;
return false;
}
二叉堆是一个部分有序的完全二叉树,因为完全二叉树可以用数组来维护,所以遍历起来比较快速
下图描述了一个小顶堆的上浮排序过程,最终得到一个顶层一定最小,而且每一层根节点都小于下一层孩子节点的完全二叉树
但是同一层是不存在优先级顺序的。
.cpp源文件
/*定义队列长度,初始化队列*/
template<typename T>
MaxPQ<T>::MaxPQ(int maxN)
{
pq.assign(maxN+1, 0);
}
template<typename T>
MaxPQ<T>::~MaxPQ()
{
}
template<typename T>
bool MaxPQ<T>::isEmpty()
{
if (N)
return false;
return true;
}
/*插入元素,并进行排序*/
template<typename T>
void MaxPQ<T>::insert(T val)
{
pq[++N] = val;
swim(N);
}
/*删除堆顶,并重构堆*/
template<typename T>
T MaxPQ<T>::delMax()
{
T max = pq[1];
exchan(pq,1,N--);
sink(1);
return max;
}
/*上浮操作,与父节点比较,并判断是否进行易位*/
template<typename T>
void MaxPQ<T>::swim(int k)
{
while (k>1 && pqless(pq,k/2,k)) {
exchan(pq,k/2,k);
k = k / 2;
}
}
/*下沉操作,需要与较大的子节点比较,并判断是否进行易位*/
template<typename T>
void MaxPQ<T>::sink(int k)
{
while (2*k<=N) {
int j = 2 * k;
if (j < N && pqless(pq, j, j + 1))
j++;
if (!pqless(pq, k, j))
break;
exchan(pq,k,j);
k = j;
}
}
/*小顶对,修改了上浮和下沉的逻辑,其他操作与大顶堆一致*/
template<typename T>
MinPQ<T>::MinPQ(int maxN)
{
pq.assign(maxN+1, 0);
}
template<typename T>
MinPQ<T>::~MinPQ()
{
}
template<typename T>
bool MinPQ<T>::isEmpty()
{
if (N)
return false;
return true;
}
template<typename T>
void MinPQ<T>::insert(T val)
{
pq[++N] = val;
swim(N);
}
template<typename T>
T MinPQ<T>::delMin()
{
T max = pq[1];
exchan(pq, 1, N--);
sink(1);
return max;
}
template<typename T>
void MinPQ<T>::swim(int k)
{
while (k > 1 && !pqless(pq, k / 2, k)) {
exchan(pq, k / 2, k);
k = k / 2;
}
}
template<typename T>
void MinPQ<T>::sink(int k)
{
while (2 * k <= N) {
int j = 2 * k;
if (j < N && !pqless(pq, j, j + 1))
j++;
if (pqless(pq, k, j))
break;
exchan(pq, k, j);
k = j;
}
}
测试用例main.cpp
#if PQUEUE_TEST
MaxPQ<int> maxpq(5);
MinPQ<int> minpq(5);
maxpq.insert(1);
maxpq.insert(2);
maxpq.insert(3);
maxpq.insert(4);
maxpq.insert(5);
minpq.insert(3);
minpq.insert(1);
minpq.insert(2);
minpq.insert(4);
minpq.insert(5);
cout << maxpq.delMax() << " " << minpq.delMin() << endl;
cout << maxpq.delMax() << " " << minpq.delMin() << endl;
cout << maxpq.delMax() << " " << minpq.delMin() << endl;
cout << maxpq.delMax() << " " << minpq.delMin() << endl;
cout << maxpq.delMax() << " " << minpq.delMin() << endl;
#endif
system("pause");
return 0;
预期输出结果:
对一个大顶堆和一个小顶堆实现的优先队列分别随机插入了5个相同的元素,那么每次获取队首元素所得到
的两组输出结果应该是互为逆序
运行结果: