Priority_Queue 优先队列 C++学习笔记

时间:2022-04-18 17:39:37
priority_queue优先队列容器与队列一样,只能从队尾添加(插入)元素,从队头(队首)删除元素。但他有一个特性,就是队列中最大的元素总是位于队首,所以出队时,并非按先进先出的原则进行,而是将当前队列中最大的元素出队。这点类似与给队列里的元素进行了由大到小的顺序排序。元素的比较规则默认为按元素的值的由大到小排序;当然,可以重载“<”操作符来重新定义比较规则;

使用priority_queue需要声明头文件#include <queue>;

优先队列的使用方法

优先队列包含入队push()(插入元素),出队pop()(删除元素),读取队头元素top(),判断队列是否为空empty()和读取队列元素数量size()等方法;
下例程序具体说明了优先队列的使用方法:
#include <iostream>
#include <queue>
using namespace std;


int main(int argc, char *argv[])
{
    //定义优先队列对象,元素类型为整型;
    priority_queue <int> pq;
    //入队,插入新的元素;
    pq.push(1);
    pq.push(2);
    pq.push(3);
    pq.push(9);
    //返回队列中元素的个数;
    cout << pq.size() << endl;
    while(!pq.empty()) {  // == pq.empty!=true;
        //读取当前队首元素;
        cout << pq.top() << " ";
        //出队,删除队首元素;
        pq.pop();
    }
    cout << endl;
    return 0;
}


运行结果为:
4
9 3 2 1

重载“<”操作符来定义优先级

如果优先队列的元素类型是结构体,可以通过在结构体中重载“<”操作符的方法来修改优先队列的优先性;
下例程序详细说明了在结构体中重载“<”操作符的方法;

运行结果为:
Bomi 18.5
Jack 68.5
Peti 90

#include <iostream>
#include <queue>
#include <cstdio>
using namespace std;


struct Info
{
    string name;
    float score;
    //重载"<"操作符,指定优先级;
    bool operator < (const Info &a) const
    {
        //按score由小到大排列,如果要由大到小排列,使用“>”即可;
        return a.score < score;
    }
};

int main(int argc, char *argv[])
{
    freopen("data.in", "r", stdin);
    freopen("data.out", "w", stdout);
    //定义优先队列对象,元素类型为Info结构体型;
    priority_queue <Info> pq;
    Info info;  //定义结构体变量;

    //入队;
    info.name = "Jack";
    info.score = 68.5;
    pq.push(info);

    info.name = "Bomi";
    info.score = 18.5;
    pq.push(info);

    info.name = "Peti";
    info.score = 90;
    pq.push(info);
    //元素出队;
    while(!pq.empty()) {
        //返回队首元素;
        cout << pq.top().name << " " << pq.top().score << endl;
        //将队头元素弹出;
        pq.pop();
    }
    return 0;
}

重载“()”操作符来定义优先级

如果优先队列的元素不是结构体类型,则可以通过重载“()”操作符的方式来定义优先级,当然,元素是结构体类型,也可以通过重载“()”操作符的方法来定义优先级,而不是一定要在结构体内重载“<”操作符才行;

下例程序说明了重载“()”操作符的方法:

运行结果为:
1 5 9 28 34

#include <iostream>
#include <queue>
using namespace std;

//重载“()”操作符;
struct mycmp
{
    bool operator () (const int &a, const int &b)
    {
        //由小到大排列采用">"号,如果要由大到小排列,则采用"<"号;
        return a > b;
    }
};

int main(int argc, char *argv[])
{
    //定义优先队列,元素类型为Info的结构体,显式说明内部结构是vector;
    priority_queue <int, vector<int>, mycmp> pq;
    //入队;
    pq.push(1);
    pq.push(28);
    pq.push(9);
    pq.push(5);
    pq.push(34);
    //元素全部出队;
    while(!pq.empty()) {
        //返回队首元素;
        cout << pq.top() << " ";
        //删除队首元素;
        pq.pop();
    }
    cout << endl;
    return 0;
}


Windows 消息队列 ZOJ 2724

//ZheJiang University Local Contest 2006

消息队列是Windows操作系统的基石,对于每一个进程,系统维持了一个消息队列,如果一个进程发生了某一事件,如单击鼠标,文本改变,系统将增加一个消息到队列里,同时,如果队列里不是空的,那么,进程将一直循环的从队列里按优先值抓取消息,注意,数值小意味着高的优先级,在本题中,要求你模拟消息队列,把消息放到消息队列中,或从消息队列里抓取消息;
输入描述:
只有一个测试案例,每行是一条命令,“GET”或“PUT”表示从消息队列里抓取消息或把消息放入消息队列。如果是“PUT”命令,后面跟着一个字符串(表示消息名称)和两个整数(分别表示消息的参数和优先级),案例中的命令最多达60000条,注意,一条命令可以重复出现多次,如果两条命令的优先级相同,则先进入消息队列的那条先被处理;一直处理到文件结尾;
输出描述:对于每条“GET”指令,直接输出他抓取的消息的名称和参数在一行上,如果消息队列是空的,那么直接输出“EMPTY QUEUE!”,对于“PUT”指令,不需要输出什么;
Sample Input:
GET
PUT msg1 10 5
PUT msg2 10 4
GET
GET
GET
Sample Output:
EMPTY QUEUE!
msg2 10
msg1 10
EMPTY QUEUE!

解题思路:
本题是模拟Windows处理消息队列,很有意义,解题中主要遇到的问题是超时错误,根据本题的特点,宜使用优先队列容器来实现;
另外,光是用优先队列容器还不行,必须采用scanf和printf输入输出,否则还会超时,因为本题的数据量大,多达60000行字符串需要处理,需要输入输出;

The Code Follows:

<span style="font-family:Microsoft YaHei;font-size:18px;">#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;

struct Message {  //这里不要用typedef了,因为重载运算符参数类型Message不能识别;
    char Name[200];
    int data, priority;

    bool operator < (const Message a) const  //重载 < 运算符自定义排序准则;
    {
        return a.priority < priority;
    }
};

int main()
{
    priority_queue <Message> v; //定义一个优先队列的对象;
    char command[200];

    while(~scanf("%s", command)) {  //输入命令;
        Message message;
        if(strcmp(command, "GET") == 0) {
            if(v.size() == 0) {      //队列为空;
                printf("EMPTY QUEUE!\n");  //使用cout<<"EMPTY QUEUE"<<endl;不会超时;
            }else {
                printf("%s %d\n", v.top().Name, v.top().data); //使用cout<<v.top().Name<<v.top().data会超时;
                v.pop();    //出队列操作,即将当前消息清除;
            }
        }else if(strcmp(command, "PUT") == 0) {
            scanf("%s %d %d", message.Name, &message.data, &message.priority);
            v.push(message);   //入列;
        }
    }
    return 0;
}
</span>