【C++】C++ STL探索:Priority Queue与仿函数的深入解析(一)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【C++】C++ STL探索:Priority Queue与仿函数的深入解析

一、优先级队列

1.1 优先级队列介绍

[优先级队列文档介绍](priority_queue - C++ Reference (cplusplus.com))

  1. 优先队列是一个容器适配器,根据严格的弱排序标准,它的第一个元素总是它所含的元素中最大的
  2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)
  3. 优先级队列已经不能称为队列,不符合FIFO特性拉
  4. 优先队列被实现为容器适配器,容器适配器即将特定的容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素,元素从特定容器的"尾部"弹出,其称为优先队列的顶部
  5. 底层容器可以是任何标准容器类模板,也可以是特定设计的容器类,容器应该可以通过随机访问迭代器访问,并支持以下操作
  • empty():检测容器是否为空
  • size():返回容器中有效元素个数
  • front():返回容器中第一个元素的引用
  • push_back():在容器尾部插入元素
  • pop_back():删除容器尾部元素
  1. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector
  2. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_head,push_heap和pop_heap来自动完成此操作。

1.2 优先级队列使用

优先级队列属于容器适配器,并跟堆具有是否相似的结构与功能,这里可以看成堆。由于堆是通过完全二叉树进行搭建,优先级队列默认使用vector作为其底层存储数据的容器,调用库中priority_queue类使用头文件

默认情况下priority_queue是大堆

#pragma once
#include <vector>
#include <algorithm>
namespace bit
{
  template<class T>
  class Less
  {
  public:
    bool operator()(const T& x, const T& y)
    {
      return x < y;
    }
  };
  template<class T>
  class Greater
  {
  public:
    bool operator()(const T& x, const T& y)
    {
      return x > y;
    }
  };
  template<class T, class Containor = vector<T>>
  class priority_queue
  {
  public:
    void push(const T& x)
    {
      _con.push_back(x);
      adjust_up(_con.size()-1);
    }
    void adjust_up(size_t child)
    {
      size_t parent = (child - 1) / 2;
      while (child > 0)
      {
        if (_con[parent] < _con[child])
        {
          std::swap(_con[child], _con[parent]);
          child = parent;
          parent = (child - 1) / 2;
        }
        else
          break;
      }
    }
    void pop()
    {
      std::swap(_con[0], _con[_con.size()-1]);
      _con.pop_back();
      adjust_down(0);
    }
    void adjust_down(size_t parent)
    {
      size_t child = parent * 2 + 1;
      while (child  < _con.size())
      {
        if (child + 1 < _con.size() && _con[child] < _con[child + 1]) child++;
        if (_con[parent] < _con[child])
        {
          std::swap(_con[child], _con[parent]);
          parent = child;
          child = parent * 2 + 1;
        }
        else
          break;
      }
    }
    
    const T& top()
    {
      return _con[0];
    }
    size_t size()
    {
      return _con.size();
    }
    bool empty()
    {
      return _con.empty();
    }
  private:
    Containor _con;
  };
  void test()
  {
    priority_queue<int> pq;
    pq.push(3);
    pq.push(2);
    pq.push(2);
    pq.push(110);
    while (!pq.empty())
    {
      cout << pq.top() << " ";
      pq.pop();
        }
    cout << endl;
  }
}

以上就是建堆的逻辑,但是如果需要建小堆,需要去内部修改大于小于号,显得有些繁琐。对此引用了一个类模板适应不同类型,对operator()函数进行运算符重载,作为控制比较逻辑,其中在外部进行开关的控制。

问题:

  • 如果需要比较的逻辑,C语言中的sqort函数不行吗?

答:

  • C++不喜欢使用qsort函数,参数部分的函数指针显得很麻烦,C++喜欢使用类模板中的仿函数。

1.3 小补充:priority_queue类提供的仿函数

关于仿函数,库中已经实现好了greater和less仿函数可以直接使用,priority_queue默认使用的是less建大堆

二、仿函数

2.1 仿函数概念

仿函数是通过类中运算符重载()实现特定的功能,通过使用匿名对象使用,它的对象可以想函数一样去使用,使得很难区分是调用函数还是调用匿名对象

Less lessfunc;
cout << lessfunc(1, 2) << endl;
cout <<  lessfunc.operator()(1, 2) << endl;
cout <<  Less()(1, 2) << endl;

这里推荐使用第二种和第三种,为了提高代码的可读性,第一种写法可能会引起歧义。

2.2 仿函数访问限定符

template<class T>
    class Less
    {
        public:
        bool operator()(const T& x, const T& y)
        {
            return x < y;
        }
    };
template<class T>
    class Greater
    {
        public:
        bool operator()(const T& x, const T& y)
        {
            return x > y;
        }
    };

仿函数中数据是需要公开使用,对于可以通过strcut或将class访问权限设置为public。仿函数/函数对象可以像函数一样去使用,**但是本质是类调用运算符重载。**那么为什么这么麻烦呢?直接写个函数不就行吗?还仿函数,听起来很牛逼哄哄呀!


【C++】C++ STL探索:Priority Queue与仿函数的深入解析(二)https://developer.aliyun.com/article/1617383

相关文章
|
1月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
91 10
|
1月前
|
安全 Java 程序员
Collection-Stack&Queue源码解析
Collection-Stack&Queue源码解析
80 0
|
14天前
|
自然语言处理 编译器 Linux
|
19天前
|
自然语言处理 编译器 Linux
告别头文件,编译效率提升 42%!C++ Modules 实战解析 | 干货推荐
本文中,阿里云智能集团开发工程师李泽政以 Alinux 为操作环境,讲解模块相比传统头文件有哪些优势,并通过若干个例子,学习如何组织一个 C++ 模块工程并使用模块封装第三方库或是改造现有的项目。
|
1月前
|
存储 搜索推荐 C++
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
48 2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
|
30天前
|
存储 程序员 C++
C++常用基础知识—STL库(2)
C++常用基础知识—STL库(2)
67 5
|
29天前
|
存储 算法 调度
【C++打怪之路Lv11】-- stack、queue和优先级队列
【C++打怪之路Lv11】-- stack、queue和优先级队列
30 1
|
30天前
|
存储 自然语言处理 程序员
C++常用基础知识—STL库(1)
C++常用基础知识—STL库(1)
52 1
|
1月前
|
设计模式 存储 C++
C++之stack 和 queue(下)
C++之stack 和 queue(下)
31 1
|
1月前
|
算法 安全 Linux
【C++STL简介】——我与C++的不解之缘(八)
【C++STL简介】——我与C++的不解之缘(八)