当前位置: 首页 > news >正文

【C++】stack和queue:优先级队列的使用及底层原理

目录

 

一  priority-queue 的介绍

1 priority-queue的文档介绍

2 priority-queue的介绍

二 priority-queue的使用

1 示例使用

2 核心接口实现

1 push

2 top

3 pop

4 empty

3 迭代器区间初始化

三 仿函数

1 仿函数的定义

2 仿函数可以像函数一样被使用

3 不同传参的区别

4 日期类举例

5 拓展 :sort && qsort

6 其他仿函数

1 remove remove_if

2 find_if

四 priority-queue完整代码


 

一  priority-queue 的介绍

1 priority-queue的文档介绍

priority-queue的文档介绍

翻译:

1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素 中最大的。

2. 此上下文类似于堆,在堆中可以随 时插入元素,并且只能检索最大堆元素(优先队列中位于顶 部的元素)。

3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue 提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的 顶部。

4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过 随机访问迭代器访问,并支持以下操作:

empty():检测容器是否为空

size():返回容器中有效元素个数

front():返回容器中第一个元素的引用

push_back():在容器尾部插入元素

pop_back():删除容器尾部元素

5. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue 类实例化指定容器类,则使用vector。

6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用 算法函数make_heap、push_heap和pop_heap来自动完成此操作。

2 priority-queue的介绍

priority_queue(优先队列)是 C++ 标准库中的一种容器适配器,它基于堆(heap) 数据结构实现,能够保证每次取出的元素都是当前队列中优先级最高的元素(默认是最大的元素)

第二个模板参数是容器适配器(容器适配器都不支持迭代器),第三个参数是仿函数(后面讲解)

核心接口:

 


二 priority-queue的使用

1 示例使用

#include<queue>
#include<iostream>
using namespace std;int main()
{//priority_queue<int> pq; //默认是大的优先级高(大堆)priority_queue<int, deque<int>, greater<int>> pq;//调整小的优先级高pq.push(3);pq.push(1);pq.push(5);pq.push(7);pq.push(2);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;
}

输出结果为1,2,3,5,7  为小堆

2 核心接口实现

priority-queue的私有成员变量只有一个:

private:Container _con;

1 push

任何一个数组都可以看作完全二叉树----->算出下标关系

用Push需要用到向上调整算法,因为需要确保它依然是大堆,如果忘了的同学可以看博主之前这一篇复习一下:

【数据结构】二叉树和堆

void push(const T& x){_con.push_back(x);adjust_up(_con.size() - 1);}

从最后一个位置插入,开始向上调整算法:最后一个位置是size()-1

void adjust_up(int child)
{int parent = (child-1)/2;while(child > 0){if(_con[child] > _con[parent]){swap(_con[child] , _con[parent]);//C++算法库中有,不需要自己实现child = parent;//继续向上调整,把父亲给孩子parent = (child-1)/2;}else{break;}}
}

上图假设插入的数字是75,向上调整结束之后,75会在根节点的位置

注意:大堆只是父节点比孩子节点大,但是不一定是递减排序!

2 top

const T& top(){return _con[0];}

3 pop

先交换根节点和最后一个节点,尾删。之后从0节点开始向下遍历,确保删除之后依然是大堆

void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();//尾删adjust_down(0);//从0节点开始向下调整}

void adjust_down(inr parent)
{int child = parent*2+1;//左孩子while(child < _con.size()){if(child+1 < _con.szie() && _con[child+1] > _con[child]){++child;//默认左孩子大,但如果右孩子比左孩子大,则++child}if(_con[child] > _con[parent]){swap[_con[child],_con[parent]);parent = child;//继续向下调整child = parent*2+!;}else{break;}}
}  

那怎么从大堆变成小堆:只用将第二个if语句的大于号换成小于号

4 empty

bool empty(){return _con.empty();}

 

3 迭代器区间初始化

priority-queue支持迭代器区间初始化,但是栈和队列都不支持

迭代器区间,可以传数组,也可以传指向数组的指针

template <class InputIterator>
priority_queue(InputIterator first, InputIterator last):_con(first, last)
{// 建堆for (int i = (_con.size()-1-1)/2; i >= 0; i--)//向下调整算法建堆,从最后一个节点的父节点开始{adjust_down(i);}
}

 


三 仿函数

1 仿函数的定义

在 C++ 中,仿函数(Functor) 也称为函数对象(Function Object),是一种 “行为类似函数” 的对象。它的核心是通过在类或结构体中重载 operator() 运算符,使得该类的实例可以像普通函数一样被调用。

仿函数的本质

仿函数不是函数,而是一个对象,但它可以通过 对象名(参数) 的形式调用,就像函数调用一样

仿函数就是一个类,只是仿函数重载了一个特殊的运算符:(),只要重载了这个运算符的类都可以称为仿函数

仿函数的使用在其他地方没有什么用,但是在这里,可以把他看成一个开关,当是小于号的时候是大堆,是大于号的时候是小堆

2 仿函数可以像函数一样被使用

#include <iostream>
using namespace std;template <class T> 
struct Less
{bool operator() (const T& x, const T& y) const { return x < y; }
};int main()
{Less<int> less;cout << less(1, 2) << endl;       // 方式1:像函数一样调用cout << less.operator()(1, 2) << endl;  // 方式2:显式调用operator()return 0;
}

代码解释

  1. 仿函数(函数对象)的定义struct Less 是一个模板结构体,内部重载了 operator() 运算符。这使得 Less 的对象可以像函数一样被调用,因此称为 “函数对象”。

  2. 核心逻辑operator() 的功能是比较两个值 x 和 y,返回 x < y 的结果(即 true 或 false)。

  3. 使用方式

    • 先创建 Less<int> 类型的对象 less(指定模板参数为 int,表示比较整数)。
    • 两种调用方式:
      • less(1, 2):简化写法,编译器会自动转换为调用 operator()
      • less.operator()(1, 2):显式调用重载的运算符

 

3 不同传参的区别

(1)priority-queue.h中

// 强制编译器生成默认构造函数
priority_queue() = default;  // 注意:是default,不是defultvoid adjust_up(int child)
{Compare com;  // 创建仿函数对象,用于比较int parent = (child - 1) / 2;while (child > 0){// 调用仿函数比较父节点和子节点if (com(_con[parent], _con[child]))  // 注意:此处缺少闭合括号{swap(_con[child], _con[parent]);  // 注意:是swap,不是sawpchild = parent;parent = (child - 1) / 2;}else{break;}}
}

(2)test.cpp

#include "priority_queue.h"int main()
{int a[] = { 30,4,2,66,3 };bit::priority_queue<int> pq(a, a+5);//建大堆//bit::priority_queue<int, vector<int>, bit::Greater<int>> pq(a, a + 5);//建小堆pq.push(3);pq.push(1);pq.push(5);pq.push(7);pq.push(2);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;
}

注释那一行是建小堆,上一行是建大堆

由此我们可以看出,当要建大堆的时候,传一个参数,调用的是Less,建小堆的时候,传三个参数,调Greater

但是我们还需要实现less和Greater

4 日期类举例

// 仿函数
// 日期类:比较大小
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d) const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d) const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}
};
int main()
{jq::priority_queue<Date*, vector<Date*>, PDateLess> pq;  // 现在就不再是按指针去比较,而是按指针变量指向的内容去比较的// new出来的地址带有很强的随机性pq.push(new Date(2025, 10, 18));pq.push(new Date(2025, 10, 19));pq.push(new Date(2025, 10, 17));while (!pq.empty()){cout << *pq.top() << " ";pq.pop();}cout << endl;
}

但是我们运行出来之后发现每次运行的结果都不一样。这是因为new开辟的地址有很强的随机性,比较的时候是按照指针去比较,没有意义。所以我们需要自己去实现仿函数控制。

// 自定义仿函数:比较Date*指针指向的对象大小(按<比较)
struct PDateLess
{bool operator()(const Date* p1, const Date* p2) const{// 解引用指针,比较实际Date对象return *p1 < *p2; }
};

 

5 拓展 :sort && qsort

vector<int> v1 = {1,2,3,4,5,6}greater<int> gt; // 降序
// sort(v1.begin(), v1.end()); // 升序
// sort(v1.begin(), v1.end(), gt); //用仿函数比较更加灵活
sort(v1.begin(), v1.end(), greater<int>()); //加了()for (auto e : v1)
{cout << e << " ";
}
cout << endl;// 输出: 6 5 4 3 2 1

在C语言中,qsort是函数指针

看开口方向:<  升序           >降序

 

6 其他仿函数

1 remove remove_if

// remove: 查找 + 删除 (find + erase)// remove_if (也是一个仿函数)list<int> lt1 = { 1,6,1,7,3,8,9,3 };lt1.remove(1); // 默认的remove,给一个值进行删除for (auto e : lt1){cout << e << " ";}cout << endl;// 输出: 6 7 3 8 9 3return 0;
}

2 find_if

 


四 priority-queue完整代码

#pragma once
#include<vector>namespace bit
{// 默认大的优先级高template<class T, class Container = std::vector<T>>class priority_queue{public:template <class InputIterator>priority_queue(InputIterator first, InputIterator last):_con(first, last){// 建堆for (int i = (_con.size()-1-1)/2; i >= 0; i--){adjust_down(i);}}// 强制编译器生成默认构造priority_queue() = default;void adjust_up(int child){int parent = (child - 1) / 2;while (child > 0){if (_con[child] > _con[parent]){swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}void adjust_down(int parent){size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && _con[child + 1] > _con[child]){++child;}if (_con[child] > _con[parent]){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}void push(const T& x){_con.push_back(x);adjust_up(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}const T& top(){return _con[0];}bool empty(){return _con.empty();}private:Container _con;};
}

 

 

http://www.dtcms.com/a/523895.html

相关文章:

  • 兰州营销型网站建设优化游戏的软件
  • 廊坊做网站的公司专门做孕婴用品的网站
  • 3. char、字符串、字符串数组、二维字符数组、char[] 的区别与联系
  • 数据结构C语言
  • RTX5060Ti安装cuda加速的openCV
  • 金融网站建设重庆网站建设电脑版
  • 超越图像:机器学习之生成对抗网络(GAN)在时序数据增强与异常检测中的深度实践
  • C# 企业微信机器人消息推送
  • 原生日历表
  • 做网站购买服务器多少钱三亚房地产网站制作
  • 新网站的建设工作织梦软件怎么使用域名做网站
  • 暖手宝方案开发,暖手宝MCU控制方案开发设计
  • SpringCloud启动——MybatisPlus(MP)
  • 图解Java链表反转:迭代法详解
  • SOGS压缩技术
  • SQLiteStudio下载安装图解教程(附安装包)
  • 【图像超分】论文复现:轻量化超分 | RLFN的Pytorch源码复现,跑通源码,整合到EDSR-PyTorch中进行训练、测试
  • 吉利汽车携手阿里云函数计算,打造新一代 AI 座舱推理引擎
  • 基于mormot.net.async.pas实现一个纯粹的Socket Server
  • FastReport .NET 2026.1 全新发布: 统一Demo中心、全新Ribbon界面、Excel公式导出、Word图像质量设置等重磅升级!
  • 网站后台上传图片脚本错误个人博客大全
  • 郑州水晶奖杯制作wordpress加载优化
  • 【计算机网络笔记】第二章 应用层 (Application Layer)
  • 东营聊城网站建设seo门户网站建设
  • DigitalOcean Gradient™ 平台上线 fal 四款多模态 AI 模型:快速生成图像与音频
  • 5、服务器互连技术(小白入门版)
  • 我爱学算法之—— 分治-归并
  • 济南高新区 网站制作wordpress直接购买
  • 织梦网站首页幻灯片不显示新华美玉官方网站在线做
  • 蓝色星球如何打造能与企业共同进化的灵活系统