【C++】优先队列(Priority Queue)全知道

亲爱的读者朋友们😃,此文开启知识盛宴与思想碰撞🎉。

快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。

 


目录

一、前言

二、优先队列(Priority Queue):排队也有 “特权”?

(一)优先队列是啥?

(二)C++ 里怎么用优先队列?

(三)优先队列内部到底咋回事?

1. 秘密武器:二叉堆(Binary Heap)

2. 元素进出的 “魔法”

插入(push)元素

删除(pop)元素

(四)优先队列都在哪里大显身手?

1. 任务调度:谁先谁后安排好

2. 图算法中的 “指路明灯”

3. 数据压缩:让数据 “瘦身” 有妙招


一、前言

在 C++ 编程的奇妙世界里,数据结构就像是一个个功能各异的工具,帮助我们高效地处理和组织数据。而在众多的数据结构中,优先队列(Priority Queue)有着独特且重要的地位。它们各自有着独特的工作方式和应用场景,就如同特殊的钥匙,能为我们开启解决不同编程难题的大门。

👉 priority_queue文档介绍 


二、优先队列(Priority Queue):排队也有 “特权”?

(一)优先队列是啥?

想象一下,你在一个超级特别的队伍里。这个队伍里的人可不是按照先来后到排队的哦😉,而是每个人都有一个 “重要程度” 的标签🧾。比如说,在医院的急诊室,重伤的病人就比轻伤的病人更 “重要”,会优先得到救治。这就是优先队列的基本概念啦!它是一种数据结构,元素们按照自己的优先级排队,优先级最高的元素总是站在队伍的最前面,等着被处理。

(二)C++ 里怎么用优先队列?

  1. 标准模板库(STL)来帮忙
    C++ 的 STL 给我们提供了一个超方便的priority_queue类。要用它的话,先把<queue>头文件包含进来哦🧐。就像这样:
    #include <iostream>
    #include <queue>int main() {std::priority_queue<int> pq;pq.push(5);pq.push(10);pq.push(3);std::cout << "队首元素(最大值): " << pq.top() << std::endl;pq.pop();std::cout << "队首元素(最大值): " << pq.top() << std::endl;return 0;
    }

    这里我们创建了一个存整数的优先队列,默认情况下,它就像一个 “选大比赛”,最大的数在队首哦😎。每次push进去一个数,它就会自动按照大小排好队,top就能看到队首的最大值,pop就把最大值请出去啦。

  2.  想自定义规则?没问题!
    要是你觉得默认的 “选大” 或者 “选小” 规则不合心意,也可以自己定规则哦😏。这时候就要用到比较函数或者函数对象啦。看下面这个例子,我们来创建一个小顶堆,让最小的数在队首

    #include <iostream>
    #include <queue>struct Compare {bool operator()(int a, int b) {return a > b;}
    };int main() {std::priority_queue<int, std::vector<int>, Compare> pq;pq.push(5);pq.push(10);pq.push(3);std::cout << "队首元素(最小值): " << pq.top() << std::endl;pq.pop();std::cout << "队首元素(最小值): " << pq.top() << std::endl;return 0;
    }

    我们定义了一个Compare结构体,里面的operator()函数就是我们的比较规则。这样,优先队列就会按照我们定的规则来排队啦。

 

(三)优先队列内部到底咋回事?

1. 秘密武器:二叉堆(Binary Heap)

其实呀,priority_queue背后是靠二叉堆这个小助手来干活的呢😎。二叉堆就像一棵特别的树,每个节点都有自己的任务哦。对于大顶堆来说,父节点就像个小队长,它的值得比子节点的值大,这样才能保证队里最大的值在最上面呀。小顶堆呢,就刚好相反,父节点的值要小于等于子节点的值。

 下面是个简单示意二叉堆节点结构体的代码,实际 STL 里的实现更复杂哦:

template<typename T>
struct BinaryHeapNode {T value;BinaryHeapNode<T>* left;BinaryHeapNode<T>* right;BinaryHeapNode(T val) : value(val), left(nullptr), right(nullptr) {}
};
2. 元素进出的 “魔法”
插入(push)元素

当有新元素要加进这个特别的队伍时,它会先站到队伍最后面哦。然后呢,它就开始和前面的人比,如果它比前面的人更 “重要”(按堆的规则),就和前面的人换位置,一直这么比下去,直到找到自己合适的位置,这就叫 “上溯(sift up)” 啦,就好像新队员要在队伍里找到自己的等级一样呢🧐。

下面是个简单示例代码片段,展示插入元素并维护二叉堆性质(假设已有上面那个二叉堆节点结构体定义哦):

template<typename T>
void siftUp(BinaryHeapNode<T>* node) {while (node!= nullptr && node->parent!= nullptr && node->value > node->parent->value) {// 交换节点和其父节点的值swap(node->value, node->parent->value);node = node->parent;}
}template<typename T>
void pushToHeap(BinaryHeapNode<T>* root, T value) {// 创建新节点并插入到堆的末尾BinaryHeapNode<T>* newNode = new BinaryHeapNode<T>(value);if (root == nullptr) {root = newNode;} else {// 找到合适的位置插入新节点(这里简单假设插入到最后一个叶子节点位置)BinaryHeapNode<T>* current = root;while (current->left!= nullptr && current->right!= nullptr) {current = current->left;}if (current->left == nullptr) {current->left = newNode;} else {current->right = newNode;}// 对新插入的节点进行上溯操作siftUp(newNode);}
}
删除(pop)元素

当要处理队首元素(也就是pop操作)时,队伍最后面的那个人会跑到队首来。但他可能不符合队首的要求呀,所以他就得和下面的人比,如果下面的人比他更 “适合” 队首,就和下面的人换位置,一直比到他找到自己合适的位置为止,这就是 “下溯(sift down)” 的过程啦😉。

 下面是个简单示例代码片段,展示删除堆顶元素并维护二叉堆性质(同样假设已有上面那个二叉堆节点结构体定义哦):

template<typename T>
void siftDown(BinaryHeapNode<T>* node) {while (node!= nullptr) {BinaryHeapNode<T>* largest = node;if (node->left!= nullptr && node->left->value > largest->value) {largest = node->left;}if (node->right!= nullptr && node->right->value > largest->value) {largest = node->right;}if (largest!= node) {// 交换节点和最大子节点的值swap(node->value, largest->value);node = largest;} else {break;}}
}template<typename T>
T popFromHeap(BinaryHeapNode<T>* root) {if (root == nullptr) {throw runtime_error("堆为空");}T rootValue = root->value;// 将堆的最后一个节点的值赋给堆顶BinaryHeapNode<T>* lastNode = findLastNode(root);root->value = lastNode->value;// 删除最后一个节点(这里简单假设能正确删除,实际可能更复杂)delete lastNode;// 对新的堆顶节点进行下溯操作siftDown(root);return rootValue;
}

在这代码里,popFromHeap函数就是用来删二叉堆的堆顶元素的,删完后用siftDown函数来保证二叉堆还是符合大顶堆的条件呢。

(四)优先队列都在哪里大显身手?

1. 任务调度:谁先谁后安排好

在操作系统或者任务管理系统里呀,任务就像一群等着被处理的小士兵👨‍✈️。每个任务都有自己的优先级,比如紧急任务优先级高,普通任务优先级低。优先队列就像个聪明的指挥官,把任务按优先级排好队,处理器就可以按顺序先处理重要的任务啦,这样系统就能高效地运行咯,就跟快递分拣中心会优先处理加急件一样呢😎。

下面是个简单的任务调度模拟代码示例,用优先队列来实现任务按优先级排序处理哦:

#include <iostream>
#include <queue>
#include <string>using namespace std;// 任务结构体
struct Task {string name;int priority;Task(const string& n, int p) : name(n), priority(p) {}
};// 自定义比较函数,按照任务优先级从高到低排序
struct TaskCompare {bool operator()(const Task& t1, const Task& t2) {return t1.priority < t2.priority;}
};int main() {// 创建任务优先队列priority_queue<Task, vector<Task>, TaskCompare> taskQueue;// 添加一些任务到队列taskQueue.push(Task("任务1", 5));taskQueue.push(Task("任务2", 3));taskQueue.push(Task("任务3", 8));// 模拟处理器处理任务while (!taskQueue.empty()) {Task currentTask = taskQueue.top();cout << "正在处理任务: " << currentTask.name << ",优先级: " << currentTask.priority << endl;taskQueue.pop();}return 0;
}

 

 在这个例子里,我们先定义了Task结构体来表示任务,有任务名字和优先级两个属性哦。然后通过自定义的TaskCompare比较函数,让优先队列按任务优先级从高到低给任务排队。最后模拟处理器依次处理队列里的任务呢。

2. 图算法中的 “指路明灯”

在一些图算法里呀,比如 Dijkstra 算法求单源最短路径,优先队列可真是个大功臣呢🧐。想象一下你在一个迷宫里找出口,每个路口都有不同的距离标记。算法得不断选离起点最近的未访问路口继续探索呀,优先队列就能很快告诉算法哪个路口离起点最近,就像个导航仪一样,让算法能高效地找到最短路径呢😉。

下面是个简单的 Dijkstra 算法示例,用优先队列来辅助找到从一个源点到其他节点的最短路径哦(这只是个简化示例,实际应用可能更复杂啦):

#include <iostream>
#include <queue>
#include <vector>
#include <unordered_map>using namespace std;// 节点结构体
struct Node {int id;vector<Node*> neighbors;int distance;Node(int i) : id(i), distance(INT_MAX) {}
};// 自定义比较函数,按照节点距离从小到大排序
struct NodeCompare {bool operator()(const Node* n1, const Node* n2) {return n1->distance > n2->distance;}
};// Dijkstra算法实现
void dijkstra(Node* source) {// 创建优先队列,按照节点距离排序priority_queue<Node*, vector<Node*>, NodeCompare> pq;// 将源点加入队列,并设置其距离为0source->distance = 0;pq.push(source);// 用于记录已访问过的节点unordered_map<int, bool> visited;while (!pq.empty()) {Node* currentNode = pq.top();pq.pop();// 如果节点已经访问过,跳过if (visited[currentNode->id]) {continue;}// 标记当前节点为已访问visited[currentNode->id] = true;// 更新当前节点邻居的距离for (Node* neighbor : currentNode->neighbors) {int newDistance = currentNode->日前距离 + 1;if (newDistance < neighbor->distance) {neighbor->distance = newDistance;pq.push(neighbor);}}}
}int main() {// 创建一些节点Node* node1 = new Node(1);Node* node2 = new Node(2);Node* node3 = new Node(3);// 构建节点之间的连接关系node1->日前邻居.push_back(node2);node1->日前邻居.push_back(node3);node2->日前邻居.push_back(node3);// 运行Dijkstra算法dijkstra(node1);// 输出节点到源点的距离cout << "节点2到源点的距离: " << node2->distance << endl;cout << "节点3到源点的距离: " << node3->distance << endl;return 0;
}

 在这个例子里,我们先定义了Node结构体来表示图中的节点,有节点 ID、邻居节点列表和到源点的距离这些属性哦。然后通过自定义的NodeCompare比较函数,让优先队列按节点距离从小到大给节点排队。接着在 Dijkstra 算法里,利用优先队列高效地选离源点最近的未访问节点进行处理,这样就能找到从源点到其他节点的最短路径啦。

3. 数据压缩:让数据 “瘦身” 有妙招

在哈夫曼编码这个数据压缩方法里呀,优先队列也发挥着重要作用呢😎。它就像个聪明的小助手,帮我们构建哈夫曼树。在构建过程中,它会选出现频率最低的两个字符,把它们合并成一个新的 “组合字符”,然后继续选频率最低的进行合并,直到构建出完整的哈夫曼树。最后根据这棵树给每个字符分配不同长度的编码,这样就能让数据占用更少的空间啦😃。

 下面是个简单的哈夫曼编码构建示例,用优先队列来辅助构建哈夫曼树哦(这也是个简化示例,实际应用可能更复杂啦):

#include <iostream>
#include <queue>
#include <vector>
#include <unordered_map>using namespace std;// 哈夫曼树节点结构体
struct HuffmanNode {char data;int frequency;HuffmanNode* left;HuffmanNode* left右;HuffmanNode(char d, int f) : data(d), frequency(f), left(nullptr), left右(nullptr) {}
}// 自定义比较函数,按照节点频率从小到大排序
struct HuffmanNodeCompare {bool operator()(const HuffmanNode* n1, const HuffmanNode* n2) {return n1->frequency > n2->frequency;}
};// 构建哈夫曼树
HuffmanNode* buildHuffmanTree(const unordered_map<char, int>& frequencyMap) {// 创建优先队列,按照节点频率排序priority_queue<HuffmanNode*, vector<H夫曼Node*>, HuffmanNodeCompare> pq;// 将每个字符及其频率对应的节点加入队列for (const auto& pair : frequencyMap) {pq.push(new HuffmanNode(pair.first, pair.second));}while (pq.size() > 1) {// 取出频率最低的两个节点HuffmanNode* leftNode = pq.top();pq.pop();HuffmanNode* rightNode = pq.top();pq.pop();// 创建新的父节点,频率为两个子节点频率之和HuffmanNode* parentNode = new HuffmanNode('\0', leftNode->frequency + rightNode->frequency);parentNode->left = leftNode;parentNode->right = rightNode;// 将父节点加入队列pq.push(parentNode);}return pq.top();
}int main() {// 假设这里有个字符频率映射表unordered_map<char, int> frequencyMap = {{'a', 5},{'b', 3},{'c', 8}};// 构建哈夫曼树HuffmanNode* huffmanTree = buildHuffmanTree(frequencyMap);// 这里可以根据哈夫曼树进一步处理,比如生成编码等,但为了简化先不展示啦return 0;
}

 在这个例子里,我们先定义了HuffmanNode结构体来表示哈夫曼树的节点,有字符数据、频率、左右子节点这些属性哦。然后通过自定义的HuffmanNodeCompare比较函数,让优先队列按节点频率从小到大给节点排队。接着在构建哈夫曼树的过程中,利用优先队列选出频率最低的两个节点合并成新节点,一直重复这个过程直到构建出完整的哈夫曼树呢。


如果本文对你有帮助欢迎关注我👉【A Charmer】  

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/888181.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STL:相同Size大小的vector和list哪个占用空间多?

在C中&#xff0c;vector和list是两种不同的序列容器。vector底层是连续的内存&#xff0c;而list是非连续的&#xff0c;分散存储的。因此&#xff0c;vector占用的空间更多&#xff0c;因为它需要为存储的元素分配连续的内存空间。 具体占用多少空间&#xff0c;取决于它们分…

GPT vs Claude到底如何选?

美国当地时间6月20日&#xff0c;OpenAI的“劲敌”Anthropic公司发布了最新模型Claude 3.5 Sonnet。据Anthropic介绍&#xff0c;该模型是Claude 3.5系列模型中的首个版本&#xff0c;也是Anthropic迄今为止发布的“最强大、最智能”的模型。它不仅在性能上超越了竞争对手和自家…

OGRE 3D----5. OGRE和QML事件交互

在现代图形应用程序开发中,OGRE(Object-Oriented Graphics Rendering Engine)作为一个高性能的3D渲染引擎,广泛应用于游戏开发、虚拟现实和仿真等领域。而QML(Qt Modeling Language)则是Qt框架中的一种声明式语言,专注于设计用户界面。将OGRE与QML结合,可以充分利用OGR…

mysql系列2—InnoDB数据存储方式

背景 本文将深入探讨InnoDB的底层存储机制&#xff0c;包括行格式、页结构、页目录以及表空间等核心概念。通过全面了解这些基础概念&#xff0c;有助于把握MySQL的存储架构&#xff0c;也为后续深入讨论MySQL的索引原理和查询优化策略奠定了基础。 1.行格式 mysql中数据以行…

matlab2024a安装

1.开始安装 2.点击安装 3.选择安装密钥 4.接受条款 5.安装密钥 21471-07182-41807-00726-32378-34241-61866-60308-44209-03650-51035-48216-24734-36781-57695-35731-64525-44540-57877-31100-06573-50736-60034-42697-39512-63953 6 7.选择许可证文件 8.找许可证文件 9.选…

交换机四大镜像(端口镜像、流镜像、VLAN镜像、MAC镜像)应用场景、配置实例及区别对比

在网络管理中&#xff0c;端口镜像、流镜像、VLAN镜像和MAC镜像都是用于监控和分析网络流量的重要技术。 端口镜像&#xff08;Port Mirroring&#xff09; 定义&#xff1a;端口镜像是将一个或多个源端口的流量复制到一个目标端口&#xff0c;以便于网络管理员能够监控和分析…

JVM知识点学习-1

学习视频&#xff1a;狂神说Java 类加载器和双亲委派机制 类加载器 作用&#xff1a;加载Class文件 流程&#xff1a;这里的名字car1。。在栈里面&#xff0c;但是数据在堆里面 类加载器的几个类型&#xff1a; 虚拟机自带的类加载器&#xff1b;启动类&#xff08;根Boot…

Linux下的三种 IO 复用

目录 一、Select 1、函数 API 2、使用限制 3、使用 Demo 二、Poll 三、epoll 0、 实现原理 1、函数 API 2、简单代码模板 3、LT/ET 使用过程 &#xff08;1&#xff09;LT 水平触发 &#xff08;2&#xff09;ET边沿触发 4、使用 Demo 四、参考链接 一、Select 在…

python学习笔记 - python安装与环境变量配置

目录 前言1. 版本选择1.1 什么版本合适&#xff1f;1.2 版本越新越好吗&#xff1f;1.3 维护中的大版本里&#xff0c;选择最早的好吗&#xff1f;1.4 我的选择1.5 Python 发布周期1.6 Python维护中的版本及截止时间 2. 安装包下载2.1 官网地址2.2 下载安装包3. 环境安装3.1 新…

管理表空间和数据文件(二)

只读表空间 使用以下命令将表空间设置为只读模式&#xff1a; ALTER TABLESPACE userdata READ ONLY;必须等到TABLESPACE所有的过程都commit&#xff1b;才能可以执行成功。 导致检查点 Causes a checkpoint 意思是将内存中的数据&#xff08;如缓冲区中的更改&#xff09;写…

解决el-card上绑定@click事件,点击无效

解决&#xff1a; 在click后面加一个.native的修饰符即可 解释&#xff1a; .native 修饰符的作用&#xff1a;告诉 Vue&#xff0c;在绑定事件时&#xff0c;使用原生的 DOM 事件&#xff0c;而不是 Vue 自定义的事件。 因为 el-card 作为一个 Element UI 组件&#xff0c;默认…

AD7606使用方法

AD7606是一款8通道最高16位200ksps的AD采样芯片。5V单模拟电源供电&#xff0c;真双极性模拟输入可以选择10 V&#xff0c;5 V两种量程。支持串口与并口两种读取方式。 硬件连接方式&#xff1a; 配置引脚 引脚功能 详细说明 OS2 OS1 OS2 过采样率配置 000 1倍过采样率 …

蓝桥-希尔排序模板题

第一眼看到这个题还在想希尔排序模板不记得了&#xff0c;于是去网上了搜了一个&#xff0c;但是考虑到这种题只看测试点能不能通过&#xff0c;于是用Arrays方法试了一下&#xff0c;发现也可以。 1.希尔排序模板ac代码 package yunkePra;import java.util.Scanner;public cl…

机器学习6_支持向量机_算法流程

最大化&#xff1a; 限制条件&#xff1a; &#xff08;1&#xff09; &#xff08;2&#xff09; 如何求解这个对偶问题&#xff0c;同时基于对偶问题给出支持向量机算法的统一流程。 (核函数) 只要知道核函数&#xff0c;就可以求个这个最优化的对偶问题。 求解了这个对偶…

【WRF-Urban】城市冠层参数UCPs导入WPS/WRF中

城市冠层参数UCPs导入WPS/WRF中 Urban canopy parameters ingestion into WPS/ WRF关于建筑高度分布的分组数量GEOGRID.TBL 文件的配置是否需要修改 Registry 文件其他建议 参考 本博客主要总结WRF&MPAS-Aforum中有关城市冠层参数UCPs导入WPS/WRF的相关内容。原文章地址-Ur…

利用Python爬虫精准获取淘宝商品详情的深度解析

在数字化时代&#xff0c;数据的价值日益凸显&#xff0c;尤其是在电子商务领域。淘宝作为中国最大的电商平台之一&#xff0c;拥有海量的商品数据&#xff0c;对于研究市场趋势、分析消费者行为等具有重要意义。本文将详细介绍如何使用Python编写爬虫程序&#xff0c;精准获取…

Rook入门:打造云原生Ceph存储的全面学习路径(上)

文章目录 一.Rook简介二.Rook与Ceph架构2.1 Rook结构体系2.2 Rook包含组件2.3 Rook与kubernetes结合的架构图如下2.4 ceph特点2.5 ceph架构2.6 ceph组件 三.Rook部署Ceph集群3.1 部署条件3.2 获取rook最新版本3.3 rook资源文件目录结构3.4 部署Rook/CRD/Ceph集群3.5 查看rook部…

003 LVGL相关文件分析

LVGL移植相关文件&#xff1a; 显示设备接口文件 lv_port_disp_templ.c/输入设备接口文件 lv_port_indev_templ.c/h 裁剪、配置文件 lv_conf.h lv_conf.h文件内容介绍&#xff1a; 对应中文翻译版本&#xff1a; #if 1 /* 设置为1&#xff0c;以启…

汽车轮毂结构分析有哪些?国产3D仿真分析实现静力学+模态分析

本文为CAD芯智库原创&#xff0c;未经允许请勿复制、转载&#xff01; 之前分享了如何通过国产三维CAD软件如何实现「汽车/汽配行业产品设计」&#xff0c;兼容NX&#xff08;UG&#xff09;、Creo&#xff08;Proe&#xff09;&#xff0c;轻松降低企业上下游图纸交互成本等。…

关于Vscode配置Unity环境时的一些报错问题(持续更新)

第一种报错&#xff1a; 下载net请求超时&#xff08;一般都会超时很正常的&#xff09; 实际时并不需要解决&#xff0c;它对你的项目毫无影响 第二种报错&#xff1a; .net版本不匹配 解决&#xff1a;&#xff08;由于造成问题不一样&#xff0c;所以建议都尝试一次&…