优先队列总结

优先队列用于高效处理具有优先级的事件调度,包括删除最大值和插入操作。文章介绍了初级实现,如使用数组和选择/插入排序,但时间复杂度较高。为优化操作,引入了二叉堆,确保操作时间复杂度为logN。二叉堆是一种堆有序的完全二叉树,支持删除最大值和插入新值。当需要访问中间元素时,提出了索引优先队列的概念。此外,还讨论了堆排序,即通过构造堆然后不断删除最大值实现排序。文章提供了相关的堆操作和代码实现。
摘要由CSDN通过智能技术生成

优先队列

引入

在生产调度中,事件都有优先级,那么如何快速的调度最高优先级的事件成为一件需要考虑的事情。调度中有两种操作,第一种是调度最高级事件,第二种是插入事件,那么优先队列这种数据结构完美解决了这两个操作,在优先队列中这两种操作完成的任务就是删除队列中最大值和插入新值。

优先队列初级实现

那么优先队列如何实现呢?简单的可以用数组来实现,有以下两种方法:
1.数据插入随意,数据是无序的,删除时利用选择排序中的一次内循环找出最大值并删除。
2.数据插入时利用插入排序进行插入,数据是有序的,删除时直接删除最右端的值便可以。
那么这样实现的优先队列操作的时间复杂度怎么样呢?

方式插入元素删除最大元素
无序1M(最坏情况为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();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值