优先队列
引入
在生产调度中,事件都有优先级,那么如何快速的调度最高优先级的事件成为一件需要考虑的事情。调度中有两种操作,第一种是调度最高级事件,第二种是插入事件,那么优先队列这种数据结构完美解决了这两个操作,在优先队列中这两种操作完成的任务就是删除队列中最大值和插入新值。
优先队列初级实现
那么优先队列如何实现呢?简单的可以用数组来实现,有以下两种方法:
1.数据插入随意,数据是无序的,删除时利用选择排序中的一次内循环找出最大值并删除。
2.数据插入时利用插入排序进行插入,数据是有序的,删除时直接删除最右端的值便可以。
那么这样实现的优先队列操作的时间复杂度怎么样呢?
方式 | 插入元素 | 删除最大元素 |
---|---|---|
无序 | 1 | M(最坏情况为N) |
有序 | M(最坏情况为N) | 1 |
二叉堆引入
由于初级实现的优先队列的两种操作时间复杂度在N较大时效果不好,引入了二叉堆的数据结构,两种操作的数量级均可以达到logN。
概念
堆有序:树中的每个节点均大于等于它的两个子节点。
完全二叉树:除最深一层外其他层节点均达到最大值,且最深一层连续集中在左边的二叉树(完全二叉树可以与数组一一对应)
二叉堆:一组能够用堆有序的完全二叉树排序的元素,并在数组中按层级存储。
二叉堆实例:
堆操作
删除堆中的最大值:将根节点删除,并将最末尾元素放至根节点,然后将该元素进行下沉操作。
插入新值:插入至末尾,并将该元素进行上浮操作
下沉操作代码:
void sink(int k)
{
while(2*k<=N){
int j=2*k;
if(j<N&&less(j,j+1)) j++;
if(!less(k,j)) break;
exch(k,j);
k=j;
}
}
上浮操作代码:
void swim(int k)
{
while(k>1&&less(k/2,k))
{
exch(k/2,k);
k=k/2;
}
}
出队操作(删除最大值):
int delMax(vector<int>& a){
int max=a[1];//由于存储在a[1...N]中,a[0]未使用,由于上浮下沉操作需要执行乘法除法,需要利用父子节点下标关系,所以下标0使用会造成麻烦。
exch(1,N--);
sink(1);
return max;
}
入队(插入):
void insert(int k,vector<int>& a){
a[++N]=k;
swim(N);
}
至此,利用堆操作完美解决了删除最大元素和插入元素两种操作,那么我们容易知道优先队列只能访问最大的元素,如果我们需要访问中间的元素,就需要执行出列操作很多次,然后再执行很多次入列操作恢复。假设有一个公司,根据公司的员工的薪水创建了一个优先队列,比如薪水排在第1000名的员工需要涨薪水,那我们就执行1000次出列操作,然后再执行1000次入列操作,这样太麻烦了,不符合程序员的懒的优秀品质。于是,索引优先队列完美的解决了这个问题。
索引优先队列
详见
堆排序
利用优先队列这种抽象数据结构,我们引入了堆,很显然堆用来排序是可以的,一组无序的数据构造堆,然后不断执行删除操作,就可以得到有序的数组。
void sort(vector<int>& a)
{
int N=a.size();
for(int k=N/2;k>=1;k--)
sink(a,k,N);
while(N>1)
{
exch(a,1,N--);
sink(a,1,N);
}
}
完整的堆构造优先队列和堆排序代码
#include "iostream"
#include "vector"
using namespace std;
template <class T>
class PriorQueue{
private:
vector<T> pq;
int N=0;
public:
PriorQueue(vector<T> cpq){//使用vector初始化时,注意位置0是不用的。
pq=cpq;
N=cpq.size()-1;
}
void display(){
for(int i=1;i<=N;i++)
cout<<pq[i]<<endl;
}
bool isempty(){
return N==0?1:0;
}
int size(){
return N;
}
void insert(T v){
pq[++N]=v;
swim(N);
}
T delMax(){
T max=pq[1];
exch(1,N--);
sink(1);
return max;
}
bool less(int i,int j){
return pq[i]<pq[j]?1:0;
}
void exch(int i,int j){
T temp=pq[i];
pq[i]=pq[j];
pq[j]=temp;
}
void swim(int k){
while(k>1&&less(k/2,k)){
exch(k,k/2);
k=k/2;
}
}
void sink(int k,int len){
while(2*k<=len){
int j=2*k;
if(j<len&&less(j,j+1))
j++;
if(!less(k,j))break;
exch(k,j);
k=j;
}
}
void sort(){
if(N==0)return;
int len=N;
cout<<len<<endl;
for(int k=len/2;k>=1;k--){
sink(k,len);
}
while(len>1){
exch(1,len--);
sink(1,len);
}
}
};
int main(){
vector<char> a;
a.push_back(' ');
a.push_back('S');
a.push_back('O');
a.push_back('R');
a.push_back('T');
a.push_back('T');
a.push_back('E');
a.push_back('M');
a.push_back('P');
PriorQueue<char> pq(a);
//pq.display();
pq.sort();
pq.display();
}