【C++】—— 优先级队列(priority_queue)

本文围绕C++优先级队列展开,介绍其是一种容器适配器,第一个元素总是最大的,底层容器可选用vector和deque等。还提及了优先级队列的使用,并进行了模拟实现,底层用vector,同时通过自定义类型测试仿函数以实现不同比较需求。

一、优先级队列

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来自动完成此操作。

二、优先级队列的使用

#include <iostream>
#include <functional>
#include <queue>
using namespace std;


//仿函数,函数对象
//struct Less
//{
//	bool operator()(int left, int right)
//	{
//		return left < right;
//	}
//};

template<class T>
struct Less
{
	bool operator()(const T& left, const T& right)
	{
		return left < right;
	}
};

int main()
{
	//priority_queue<int> pq;//默认是传less比较生成大堆
	priority_queue<int, vector<int>, greater<int>> pq;//也可传greater,此时生成的是小堆
	pq.push(3);
	pq.push(2);
	pq.push(5);
	pq.push(0);
	pq.push(7);
	pq.push(3);

	while (pq.empty() != 1)
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;
	int a = 1; int b = 2;
	Less<int> less;//是一个对象,但是可以像使用一个函数一样使用它,称为仿函数
	cout << less(a, b) << endl;
	cout << less.operator()(a, b) << endl;
 
	return 0;
}

三、优先级队列的模拟实现

#pragma once

#include <iostream>
#include <vector>
#include <deque>
using namespace std;

template<class T, class Container = vector<T>, class Compare = less<T>>
class PriorityQueue
{
public:
	void Adjustdown(size_t parent)//向下调整算法
	{
		size_t child = parent * 2 + 1;

		while (child < _con.Size())
		{
			if (_con[child] < _con[child + 1])
				++child;
			if (_con[parent] < _con[child])
			{
				swap(_con[parent], _con[child]);
				parent = child;
				child = parent * 2 + 1;
			}
			else
			{
				break;
			}
		}
	}
	void AdjustUp(size_t child)//向上调整算法
	{
		size_t parent = (child - 1) / 2;

		while (child)
		{
			if (_con[parent] < _con[child])
			{
				swap(_con[parent], _con[child]);
				child = parent;
				parent = (child - 1) / 2;
			}
			else
			{
				break;
			}
		}
	}

	void Push(const T& x)//尾插
	{
		_con.push_back(x);
		AdjustUp(_con.size() - 1);
	}
	
	void Pop()//头删
	{
		swap(_con[0], _con[_con.Size() - 1]);
		_con.pop_back();
		Adjustdown(0);
	}

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

	size_t Size() const
	{
		return _con.size();
	}

	bool Empty() const
	{
		return _con.empty();
	}
private:
	Container _con;
};


class Goods
{
public:
	bool operator<(const Goods r) const
	{
		return _price < r._price;
	}
public:
	int _price;
	int _sales_volume;
	int _evaluation;
	int _comprehensive;
};
struct GoodsPriceLess
{
	bool operator()(const Goods& l, const Goods& r)
	{
		return l._price < r._price;
	}
};

struct GoodsPriceGreater
{
	bool operator()(const Goods& l, const Goods& r)
	{
		return l._price > r._price;
	}
};

void TestPriorityQueue()
{
	PriorityQueue<Goods, vector<Goods>, GoodsPriceLess> goodspq;
	goodspq.Push(Goods{ 1, 2, 3, 4 });
	goodspq.Push(Goods{ 4, 3, 2, 1 });

	Goods top = goodspq.Top();
	cout << top._price << endl;
	cout << top._sales_volume << endl;
	cout << top._evaluation << endl;
	cout << top._comprehensive << endl;
}

: 1、这里简单实现了优先队列的功能,底层容器用的是vector,在这里我没写优先级队列的构造函数以及析构函数,用了系统默认生成的即可。
2、测试函数中我给出了一个自定义类型,是为了测试一下自己写的仿函数,我们都知道显示生活中有很多时候我们比较时不仅仅只比较一方面,就像是一个商品我们可能会按价格比较,也可能按人气比较,或是按综合评价比较,这个时候我们用仿函数就比较方便了,我们只要按照需求写一个对应比较的仿函数就可以实现相关功能。

<think>我们讨论C++中的`priority_queue`,它是标准模板库(STL)中的一个容器适配器。它提供了一个最大堆(默认)数据结构,允许我们快速访问和删除队列中优先级最高的元素。###优先队列的常见用途1.**任务调度**:操作系统使用优先队列调度进程,优先级高的任务先执行。2.**图算法**:如Dijkstra算法中,需要快速获取当前最短路径节点。3.**数据流处理**:实时处理数据时,可能需要处理当前最重要的数据(如交易系统中价格最高的订单)。4.**模拟系统**:事件驱动的模拟,按时间顺序处理事件。5.**哈夫曼编码**:构建哈夫曼树时,需要不断从集合中取出权重最小的两个节点。###示例代码以下示例展示了`priority_queue`的基本操作:```cpp#include<iostream>#include<queue>#include<vector>#include<functional>//用于greaterusingnamespacestd;intmain(){//默认是最大堆,使用vector作为底层容器,less表示最大堆(队首元素最大)priority_queue<int>maxHeap;//创建一个最小堆priority_queue<int,vector<int>,greater<int>>minHeap;//最大堆示例cout<< "最大堆(默认):\n";maxHeap.push(30);maxHeap.push(100);maxHeap.push(25);maxHeap.push(40);while(!maxHeap.empty()){//访问并删除最大元素cout<< maxHeap.top()<< "";maxHeap.pop();}cout<< endl;//输出:100403025//最小堆示例cout<< "最小堆:\n";minHeap.push(30);minHeap.push(100);minHeap.push(25);minHeap.push(40);while(!minHeap.empty()){//访问并删除最小元素cout<< minHeap.top()<< "";minHeap.pop();}cout<< endl;//输出:253040100//自定义优先规则:假设我们有一个任务结构体structTask{intpriority;//优先级stringdesc;//任务描述//重载小于运算符,以便priority_queue可以比较//注意:在最大堆中,我们重载<,但在priority_queue中,比较是反的(因为堆顶是最大的,而默认用less)//具体来说:在最大堆中,如果a<b,则b的优先级更高(堆顶元素最大)booloperator<(constTask&other)const{//我们希望优先级数值大的在堆顶(最大堆),所以这里用小于//但注意:priority_queue中比较是“小于”比较,但堆顶是最大的元素,所以实际上是按小于的逆序(从大到小)returnpriority< other.priority;}//如果要最小堆,则使用大于比较,但需要改变比较函数对象};//使用自定义类型的最大堆priority_queue<Task>tasks;tasks.push({5,"Cleantheroom"});tasks.push({1,"Fixabug"});tasks.push({10,"Writedocumentation"});tasks.push({3,"Testthecode"});cout<< "按优先级处理任务(从高到低):\n";while(!tasks.empty()){Taskcurrent=tasks.top();tasks.pop();cout<< "优先级"<< current.priority<< ":"<< current.desc<< endl;}/*输出顺序:优先级10:Writedocumentation优先级5:Cleantheroom优先级3:Testthecode优先级1:Fixabug*/return0;}```###代码解释1.**声明优先队列**:-`priority_queue<int>maxHeap;`:创建一个整型的最大堆(默认)。-`priority_queue<int,vector<int>,greater<int>>minHeap;`:创建一个整型的最小堆,其中第二个参数是底层容器类型(通常为vector),第三个参数是比较函数(greater<int>表示最小堆)。2.**成员函数**:-`push()`:将元素加入优先队列。-`top()`:访问堆顶元素(即优先级最高的元素)。-`pop()`:删除堆顶元素。-`empty()`:检查队列是否为空。-`size()`:返回队列中元素的个数。3.**自定义类型**:-需要重载小于运算符(`operator<`)以定义排序规则(对于最大堆)。注意在最大堆中,我们定义`<`,实际上堆顶元素是最大的(即按照从大到小排序)。-如果想在自定义类型中使用最小堆,可以定义比较函数对象,并在声明优先队列时指定。例如:```cppstructTask{intpriority;stringdesc;//不重载运算符,而是定义外部比较函数};//自定义比较函数(函数对象)structCompareTask{booloperator()(constTask&a,constTask&b){//在最小堆中,我们希望优先级小的在堆顶,所以用大于比较//注意:这里与堆的机制有关:priority_queue中,如果我们希望a的优先级比b低(即b先出),则返回truereturna.priority>b.priority;}};priority_queue<Task,vector<Task>,CompareTask>minHeapTasks;```###注意事项-优先队列的底层容器默认是`vector`,但也可以使用`deque`(不常用)。-默认情况下,优先队列是最大堆,即堆顶元素最大。可以通过指定`greater`作为比较函数来创建最小堆。-当使用自定义类型时,一定要确保定义的比较规则符合需求。§§相关问题§§:1.如何用优先队列实现一个最小堆?2.优先队列的底层数据结构是什么?它如何保证操作的高效性?3.在自定义类型中,如何实现优先队列按照特定成员变量的升序或降序排列?4.优先队列与普通队列(queue)以及栈(stack)的主要区别是什么?</think> ### C++中优先队列 (`priority_queue`) 的应用与示例 `priority_queue` 是 C++标准库中的容器适配器,它提供了一种基于**优先级**的队列实现(底层通常是堆数据结构)。与普通队列的 FIFO 规则不同,`priority_queue` 按照元素优先级(默认从高到低)处理元素。 #### 主要应用场景 1. **任务调度**:处理高优先级任务 2. **路径查找算法**:如 Dijkstra 最短路径算法 3. **实时系统**:处理紧急事件 4. **数据流处理**:处理最大/最小值的元素 5. **模拟系统**:事件驱动的模拟过程 --- ### 基本用法(默认最大堆) ```cpp #include <iostream> #include <queue> #include <vector> #include <functional> // for greater int main() { // 默认最大堆(降序) std::priority_queue<int> pq; pq.push(30); pq.push(100); pq.push(25); pq.push(40); std::cout << "降序输出(默认最大堆): "; while(!pq.empty()) { std::cout << pq.top() << " "; pq.pop(); // 每次弹出最大值 } // 输出: 100 40 30 25 // 创建最小堆(升序) std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap; minHeap.push(30); minHeap.push(100); minHeap.push(25); minHeap.push(40); std::cout << "\n升序输出(最小堆): "; while(!minHeap.empty()) { std::cout << minHeap.top() << " "; minHeap.pop(); // 每次弹出最小值 } // 输出: 25 30 40 100 return 0; } ``` --- ### 自定义数据结构优先级队列 ```cpp #include <iostream> #include <queue> #include <string> // 自定义数据结构 struct Task { int priority; // 优先级值(值越小优先级越高) std::string description; // 重载<运算符定义优先级规则(最小值优先) bool operator<(const Task& other) const { return priority > other.priority; // 注意:这里使用>实现最小值优先 } }; int main() { // 优先级队列(最小值优先) std::priority_queue<Task> taskQueue; // 添加不同优先级的任务 taskQueue.push({3, "普通更新任务"}); taskQueue.push({1, "紧急修复BUG"}); taskQueue.push({2, "中等重要性需求"}); taskQueue.push({5, "后台日志清理"}); // 按优先级处理任务 std::cout << "\n任务处理顺序(值越小优先级越高):\n"; while(!taskQueue.empty()) { Task current = taskQueue.top(); std::cout << "优先级[" << current.priority << "]: " << current.description << "\n"; taskQueue.pop(); } /* 输出顺序: 优先级[1]: 紧急修复BUG 优先级[2]: 中等重要性需求 优先级[3]: 普通更新任务 优先级[5]: 后台日志清理 */ return 0; } ``` --- ### 实际应用:Dijkstra最短路径算法 ```cpp #include <iostream> #include <vector> #include <queue> #include <climits> // 使用优先队列优化Dijkstra算法 void dijkstra(const std::vector<std::vector<std::pair<int, int>>>& graph, int source) { int n = graph.size(); std::vector<int> dist(n, INT_MAX); dist[source] = 0; // 最小堆:存储(距离, 节点)对 using Node = std::pair<int, int>; std::priority_queue<Node, std::vector<Node>, std::greater<Node>> pq; pq.push({0, source}); while(!pq.empty()) { auto [d, u] = pq.top(); // C++17结构化绑定 pq.pop(); if(d != dist[u]) continue; // 跳过过时信息 for(const auto& [v, weight] : graph[u]) { int newDist = dist[u] + weight; if(newDist < dist[v]) { dist[v] = newDist; pq.push({newDist, v}); } } } // 输出结果 std::cout << "从节点" << source << "到各点的最短距离:\n"; for(int i = 0; i < n; ++i) { std::cout << "节点" << i << ": " << dist[i] << "\n"; } } int main() { // 邻接表表示图:{目标节点, 权重} std::vector<std::vector<std::pair<int, int>>> graph = { {{1, 4}, {2, 1}}, // 节点0 -> 1(4), 2(1) {{3, 2}}, // 节点1 -> 3(2) {{1, 2}, {3, 5}}, // 节点2 -> 1(2), 3(5) {{4, 3}}, // 节点3 -> 4(3) {} // 节点4 }; dijkstra(graph, 0); } ``` --- ### 性能说明 1. **时间复杂度**: - 插入元素:O(log n) - 删除顶部元素:O(log n) - 访问顶部元素:O(1) 2. **底层实现**:默认基于`vector`实现二叉堆 3. **适用场景**: - 需要频繁访问最大/最小元素 - 处理动态变化的优先级数据 - 不适合需要随机访问的场合
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值