【C++】优先级队列+反向迭代器

打印 上一主题 下一主题

主题 1789|帖子 1789|积分 5367

priority_queue的先容

通常用堆来实现,能在O(log n)的时间复杂度内插入和提取最高(或最低)优先级的元素。

   

  • 优先队列是一种容器适配器,根据严酷的弱排序标准,它的第一个元素总是它所包罗的元素中最大的(默认情况)。
  • 此上下文类似于堆,在堆中可以随时插入元素,而且只能检索最大堆元素(优先队列中位于顶部的元素)。
  • 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
  • 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作
    empty():检测容器是否为空
    size():返回容器中有效元素个数
    front():返回容器中第一个元素的引用
    push_back():在容器尾部插入元素
    pop_back():删除容器尾部元素
  • 标准容器类vector和deque满意这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。
  • 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。
    弱排序标准是一种在数学和编程中用于界说元素之间排序关系的二元关系。它要求关系满意以下三个重要性质:
1.自反性:对于任何元素a,a与自身是相等的。
2.传递性:如果a小于b,且b小于c,则a小于c。
3.连通性:对于任何两个元素a和b,要么a小于b,要么b小于a
  priority_queue的使用

1.默认情况下是大堆,其底层按照less比力;若创建小堆,将第三个模板参数换成greater的比力方式;
2.如果在priority_queue中放自界说类型的数据,用户需要在自界说类型中提供> 或者< 的重载。
数组中第k大的元素


   大堆方法:第三个模板参数直接使用less排序,利用前置–的特性,k–
将优先级队列中的前k-1个元素删除掉

    小堆方法:第三个模板参数要传入greater排序函数,运用topk问题思想,创建前k个元素的小堆,从数组的第k个元素开始遍历。如果当前元素大于堆顶元素(堆顶是最小值),则移除堆顶元素,并将当前元素参加堆。最后,堆顶元素即为数组中第k大的元素。

  priority_queue的模拟实现

仿函数

   又称函数对象,是类模板,通过重载()运算符,使得类模板的对象可以像函数一样被调用。
区分:函数模板要传对象,类模板要传参数是类型,不能加括号

  向下调整

  1. void Adjustdown(int parent)
  2. {
  3.         Compare com;
  4.         int child = 2 * parent + 1;
  5.         while (child < _con.size())
  6.         {
  7.                 if (child + 1 < _con.size() && com(_con[child],_con[child + 1]))
  8.                 {
  9.                         child++;
  10.                 }
  11.                 if (com(_con[parent],_con[child]))
  12.                 {
  13.                         swap(_con[parent], _con[child]);
  14.                         parent = child;
  15.                         child = 2 * parent + 1;
  16.                 }
  17.                 else
  18.                         break;
  19.         }
  20. }
复制代码
  通过比力器 Compare 确定堆的类型。用于从父节点开始向下调整堆结构,确保堆的性质得到满意。
作用:维护堆的性质,确保插入或移除操作后堆的结构仍然有效。
应用场景:插入新元素后或移除堆顶元素后调用。
  向上调整

  1. void Adjustup(int child)
  2. {
  3.         Compare com;
  4.         int parent = (child - 1) / 2;
  5.         while (child > 0)
  6.         {
  7.                 if (com(_con[parent],_con[child]))
  8.                 {
  9.                         swap(_con[parent], _con[child]);
  10.                         child = parent;
  11.                         parent = (child - 2) / 2;
  12.                 }
  13.                 else
  14.                 {
  15.                         break;
  16.                 }
  17.         }
  18. }
复制代码
  通过比力器 Compare 确定堆的类型。用于从子节点开始向上调整堆结构,确保堆的性质得到满意。
作用:维护堆的性质,确保插入新元素后堆的结构仍然有效。
应用场景:插入新元素后调用。
  构造函数


   复制元素:将迭代器范围内的所有元素复制到内部容器 _con 中。
构建堆:从最后一个非叶子节点开始,运用向下调整法逐步向上调整堆结构,确保堆的性质得到满意。
  删除


   交换堆顶元素与末了元素,然后调用尾删函数,或者直接size–实现删除功能,再从堆顶即下标为0的位置开始向下调整来满意堆序
  插入


   插入新元素后,从最后索引位置size()-1来向上调整满意堆序。
  自界说类测试(日期类)

  1.         class Date
  2.         {
  3.         public:
  4.                 Date(int year = 1900, int month = 1, int day = 1)
  5.                         : _year(year)
  6.                         , _month(month)
  7.                         , _day(day)
  8.                 {
  9.                 }
  10.                 bool operator<(const Date& d)const
  11.                 {
  12.                         return (_year < d._year) ||
  13.                                 (_year == d._year && _month < d._month) ||
  14.                                 (_year == d._year && _month == d._month && _day < d._day);
  15.                 }
  16.                 bool operator>(const Date& d)const
  17.                 {
  18.                         return (_year > d._year) ||
  19.                                 (_year == d._year && _month > d._month) ||
  20.                                 (_year == d._year && _month == d._month && _day > d._day);
  21.                 }
  22.                 friend ostream& operator<<(ostream& _cout, const Date& d);
  23.         private:
  24.                 int _year;
  25.                 int _month;
  26.                 int _day;
  27.         };
  28.         ostream& operator<<(ostream& _cout, const Date& d)
  29.         {
  30.                 _cout << d._year << "-" << d._month << "-" << d._day;
  31.                 return _cout;
  32.         }
  33.         struct LessPDate
  34.         {
  35.                 bool operator()(const Date* p1, const Date* p2)
  36.                 {
  37.                         return *p1 < *p2;
  38.                 }
  39.         };
  40. }
复制代码
  1. void test_priority_queue2()
  2. {
  3.         priority_queue<Date*, vector<Date*>, LessPDate> pq;
  4.         pq.push(new Date(2024, 6, 7));
  5.         pq.push(new Date(2025, 1, 19));
  6.         pq.push(new Date(2025, 10, 24));
  7.         while (!pq.empty())
  8.         {
  9.                 cout << *pq.top() << " ";
  10.                 pq.pop();
  11.          }
  12.         cout << endl;
  13.         //测试仿函数的调用,与日期类无关
  14.         Less<int>lessfunc;
  15.         cout << lessfunc(10, 24) << endl;
  16.         cout << lessfunc.operator()(10, 24) << endl;
  17. }
复制代码
  优先级队列的界说:priority_queue<Date*, vector<Date*>, LessPDate> 表示优先级队列中存储的是 Date 类型的指针,底层容器是 vector,比力器是 LessPDate。
插入元素:通过 push 方法插入三个 Date 对象。
输出元素:通过 while 循环依次输出队列中的元素,直到队列为空。

  团体代码

  1. #pragma once
  2. #include<vector>
  3. #include<functional>
  4. //仿函数/函数对象
  5. template <class T>
  6. class Less
  7. {
  8. public:
  9.         bool operator()(const T& x, const T& y)
  10.         {
  11.                 return x < y;
  12.         }
  13. };
  14. template <class T>
  15. class Greater
  16. {
  17. public:
  18.         bool operator()(const T& x, const T& y)
  19.         {
  20.                 return x > y;
  21.         }
  22. };
  23. namespace ee
  24. {
  25.         template<class T,class Container=vector<T>, class Compare=less<T>>
  26.         class priority_queue
  27.         {
  28.         private:
  29.                 void Adjustdown(int parent)
  30.                 {
  31.                         Compare com;
  32.                         int child = 2 * parent + 1;
  33.                         while (child < _con.size())
  34.                         {
  35.                                 if (child + 1 < _con.size() && com(_con[child],_con[child + 1]))
  36.                                 {
  37.                                         child++;
  38.                                 }
  39.                                 if (com(_con[parent],_con[child]))
  40.                                 {
  41.                                         swap(_con[parent], _con[child]);
  42.                                         parent = child;
  43.                                         child = 2 * parent + 1;
  44.                                 }
  45.                                 else
  46.                                         break;
  47.                         }
  48.                 }
  49.                 void Adjustup(int child)
  50.                 {
  51.                         Compare com;
  52.                         int parent = (child - 1) / 2;
  53.                         while (child > 0)
  54.                         {
  55.                                 if (com(_con[parent],_con[child]))
  56.                                 {
  57.                                         swap(_con[parent], _con[child]);
  58.                                         child = parent;
  59.                                         parent = (child - 2) / 2;
  60.                                 }
  61.                                 else
  62.                                 {
  63.                                         break;
  64.                                 }
  65.                         }
  66.                 }
  67.         public:
  68.                 priority_queue()
  69.                 {
  70.                 }
  71.                 template<class InputIterator>
  72.                 priority_queue(InputIterator first, InputIterator last)
  73.                 {
  74.                         while (first != last)
  75.                         {
  76.                                 _con.push_back(*first);
  77.                                 first++;
  78.                         }
  79.                         //建堆
  80.                         for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--)
  81.                         {
  82.                                 Adjustdown(i);
  83.                         }
  84.                 }
  85.                 void pop()
  86.                 {
  87.                         swap(_con[0], _con[_con.size() - 1]);
  88.                         _con.pop_back();
  89.                         Adjustdown(0);
  90.                 }
  91.                 void push(const T& x)
  92.                 {
  93.                         _con.push_back(x);
  94.                         Adjustup(_con.size() - 1);
  95.                 }
  96.                 const T& top()
  97.                 {
  98.                         return _con[0];
  99.                 }
  100.                 bool empty()
  101.                 {
  102.                         return _con.empty();
  103.                 }
  104.                 size_t size()
  105.                 {
  106.                         return _con.size();
  107.                 }
  108.         private:
  109.                 Container _con;
  110.         };
复制代码
反向迭代器

   顾名思义是用于反向遍历的工具。反向迭代器通常通过容器的 rbegin() 和 rend() 方法获取。rbegin() 返回指向容器最后一个元素下一个位置的反向迭代器(end),而 rend() 返回指向容器第一个元素的反向迭代器(begin)。
  1. namespace ee
  2. {
  3.         template<class Iterator,class ref,class ptr>
  4.         struct ReverseIterator
  5.         {
  6.                 typedef ReverseIterator<Iterator, ref, ptr> self;
  7.                 Iterator _it;
  8.                 ReverseIterator(Iterator it)
  9.                         :_it(it)
  10.                 { }
  11.                 ref operator*()
  12.                 {
  13.                         Iterator tmp = _it;
  14.                         return *(--tmp);
  15.                 }
  16.                 ptr operator->()
  17.                 {//返回解引用对象的地址
  18.                         return &(operator*());
  19.                 }
  20.                 self& operator++()//前置++
  21.                 {
  22.                         --_it;
  23.                         return *this;
  24.                 }
  25.                 self& operator--()
  26.                 {
  27.                         ++_it;
  28.                         return *this;
  29.                 }
  30.                 bool operator!=(const self& s)const
  31.                 {
  32.                         return _it != s._it;
  33.                 }
  34.         };
  35. }
复制代码
  一般接纳镜像对称的方式来模拟实现,即rbegin对应end,rend对应begin。
在重载*运算符时需要注意解引用的是迭代器当前指向的前一个位置,因为rbegin是最后元素的下一个位置,有大概为空会造成非法访问,或者是哨兵位头节点的位置。
++和–分别实现自减和自增的操作来满意反向迭代器的功能。
     若不使用镜像对称的方式来模拟实现反向迭代器,那么*操作符的重载就要跟随发生变革。
    vector中适配

记得包罗ReverseIterator.h头文件即可
   


  list中适配

   



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

南飓风

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表