C++中的priority_queue笔记【二】

3 篇文章 1 订阅

最近几天刷题,总是会看到题解中提到优先队列。
在这里插入图片描述
从上一次接触优先队列到现在,没想到已经过了70天了。我还是没有养成用它的好习惯。
C++中的priority_queue笔记
想来无外乎几个原因,一是目前脑子装的东西已经能够实现这一功能。我能用vector再sort解决,就不再能够想着去整什么优先队列。二来是确实没有养成相应的反馈机制。
在这里插入图片描述
因此希望通过这篇博客,再将优先队列好好捋一捋。毕竟过了这么久了。70天前的理解,说不定现在看来也存在一些不足。
当然,也会有新的想法。

priority_queue接口规范

//参考来源:http://www.cplusplus.com/reference/queue/priority_queue/?kw=priority_queue
template <class T, class Container = vector<T>,
  class Compare = less<typename Container::value_type> > class priority_queue;

c++中优先队列的模板定义:

  1. 参数类型T
  2. 底层容器类型Container,默认使用vector
  3. 排序规则Compare,默认为less

相较于STL的几大件中的containers,优先队列不被单称为一个container,而是称其为container adaptors,意思是对标准的容器做一些适配工作得到的。(更多的相关知识可以参考侯捷老师的《STL源码剖析》)

但是,其作为容器适配器,也有提供一些接口实现让我们使用、组织数据。
在这里插入图片描述

这里,完全可以通过stack进行理解。即优先队列的元素增删操作是push以及pop。然后可以访问队列顶端元素top(),可以判断是否为空.empty(),以及当前元素个数.size()
只是在插入元素的过程中,可以依照Compare重新组织当前元素的次序。

剩下的priority_queue对于基本数据类型以及自定义数据类型的使用相关。在第一篇文章中就有涉及到。
C++中的priority_queue笔记
关键还是在于:排序规则的设定。这也可以参考我的另一篇博客
C++中sort排序以及map自定义排序规则初探
将所有定义排序规则的方式都演示了一遍。

一些想法

如果这篇文章到这里就结束了,可能它的contribution就太少了。所以,我想顺便解决一下心中的一些疑惑。

  1. 按照之前的逻辑,setmultiset容器似乎已经完全能够承担优先队列的工作。为什么还要有优先队列这一容器适配器呢?
  2. 在刷题过程中,优先队列的应用场景一般是求前K个最大或最小的元素。这时候我们采取相应的小顶堆或者大顶堆即可。但是在放置元素的过程中,下面两种方式,有什么区别?
    priority_queue<int> pq;
    //1. 每次都将元素插入,然后如果size > k就pop队列顶端元素。
    for(int i = 0; i < 2*k; i++){
    	pq.push(i);
    	if(pq.size() > k) pq.pop();
    }
    //2. 每次插入前都判断一下size,如果size<k直接插入,否则判断
    //top和待插入元素的关系。符合条件再插入,否则直接丢弃
    for(int i = 0; i < 2*k; i++)
    {
    	if(pq.size() < k) pq.push(i);
    	else if(pq.top() > i) pq.push(i);
    }
    

关于疑惑1的思考

就目前的我的知识面来说,从以下的角度来思考是具有自我说服力的

set容器的底层实现是红黑树,元素的插入删除过程是对数时间复杂度的。
而优先队列是对vector或者deque的适配容器,其push操作其实是调用的底层容器的push_back()操作。而这一操作是常数时间复杂度的。

因此,set容器在查找的时候具有很大优势。而我们用优先队列的场景,往往只需要关注恰好第K个最大或者最小值top()。而之前和之后的元素,都无关紧要。这也是为什么连iterator在优先队列中都被屏蔽掉的原因。

当然,目前的思考也许还有很大的局限性。边学边否定自己,这才是成长嘛

关于疑惑2的验证

进行了两种数据的验证:

  1. 随机生成的100W的数进行插入删除
  2. 100W个相同数据的插入删除
//测试优先队列的两种插入写法
#include <queue>
#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

void generator(vector<int>& vec, int n){
    for(int i = 0; i < n; i++)
    {
        vec[i] = rand();
    }
}

void test01()
{
    priority_queue<int> pq1, pq2;
    int n = 1000000, k = 100000;
    vector<int> vec(n);
    for(int i = 0; i < n; i++)
        vec[i] = rand();
    clock_t s1,e1,s2,e2;
    s1 = clock();
    for(int i = 0; i < n; i++)
    {
        pq1.push(vec[i]);
        if(pq1.size() > k) pq1.pop();
    }
    e1 = clock();
    s2 = clock();
    for(int i = 0; i < n; i++)
    {
        if(pq2.size() < k) pq2.push(vec[i]);
        else if(pq2.top() > vec[i]) pq2.push(vec[i]);
    }
    e2 = clock();
    cout << "total time used by type1:" <<e1 - s1<< endl;
    cout << "total time used by type2:" <<e2 - s2<< endl;

}

void test02()
{
    priority_queue<int> pq1, pq2;
    int n = 1000000, k = 100000;
    vector<int> vec(n, 1);
    // generator(vec, n); //生成随机数
    clock_t s1,e1,s2,e2;
    s1 = clock();
    for(int i = 0; i < n; i++) //type1
    {
        pq1.push(vec[i]);
        if(pq1.size() > k) pq1.pop();
    }
    e1 = clock();
    s2 = clock();
    for(int i = 0; i < n; i++) //type2
    {
        if(pq2.size() < k) pq2.push(vec[i]);
        else if(pq2.top() > vec[i]) pq2.push(vec[i]);
    }
    e2 = clock();
    cout << "total time used by type1:" <<e1 - s1<< endl;
    cout << "total time used by type2:" <<e2 - s2<< endl;
    
}

int main()
{
    cout << "test01(): 1000000 random nums: " << endl;
    test01();
    cout << "test02(): 1000000 same nums(1): " << endl;
    test02();

    return 0;
}

输出结果:
在这里插入图片描述
可以发现:type1在这两种数据集的测试下都花费了更多的时间。因为必有一次插入删除操作。而type2很多时候,用一次if就替代了一次插入和删除操作。

所以我学废了,你呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值