C++深入刨析优先级队列priority_queue的使用

目录
  • 一、priority_queue的介绍
  • 二、priority_queue的使用
  • 三、priority_queue的模拟实现
  • 四、容器适配器
    • 4.1、什么是适配器
    • 4.2、适配模式
    • 4.3、STL标准库中stack和queue的底层结构

一、priority_queue的介绍

priority_queue官方文档介绍

翻译:

  • 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  • 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
  • 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为优先级队列的底层容器类,priority_queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部"弹出,其称为优先队列的顶部。
  • 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:

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

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

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

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

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

  • 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。
  • 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。

二、priority_queue的使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法(堆的创建与应用)将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆。

️️️:常用的函数接口

  • priority_queue()/priority_queue(first,last),构造优先级队列。
  • empty(),检测优先级队列是否为空,若是返回True。
  • top(),返回优先级队列中最大(最小)元素,即堆顶元素。
  • push(),在优先级队列中插入元素。
  • pop(),删除优先级队列中最大(最小)元素,即堆顶元素。

下面我们就举一个例子:

存放自定义类型,这样更具代表性!

template<class T>
class greater
{
public:
	bool operator()(const T& p1, const T& p2) const
	{
		return p1 > p2;
	}
};
struct person
{
	person(string name = "", int age = -1)
		:_name(name)
		, _age(age)
	{}
	bool operator<(const person& p) const
	{
		return _age < p._age;
	}
	bool operator>(const person& p) const
	{
		return _age > p._age;
	}
	string _name;
	int _age;
};
ostream& operator<<(ostream& out, const person& p)
{
	out << "name:" << p._name << "   " << "age:" << p._age << endl;
	return out;
}
void test02()
{
	person arr[] = { { "pxl", 23 },
					 { "dyx", 21 },
					 { "wjz", 24 },
					 { "ztd", 20 } };
	priority_queue<person, vector<person>, greater<person>> pq(arr, arr + sizeof(arr) / sizeof(arr[0]));//小堆
	pq.push(person("yzc", 22));
	cout <<"堆顶元素是:"<< pq.top() << endl;
	while (!pq.empty())
	{
		cout << pq.top() << endl;
		pq.pop();
	}
}
int main()
{
	test02();//自定义类型
	system("pause");
	return 0;
}

️️️注意点:

  • 如果存放自定义类型,我们想要自定义类型像内置类型一样进行各种运算,那么就要在类中重载相应的运算符。
  • 输出自定义类型,需要重载流输出。
  • 在priority_queue的第三个参数时,我们可以用库中的greater函数(头文件:functional),也可以我们自己写一个仿函数(如上述代码)。

三、priority_queue的模拟实现

	template<class T>
	struct less
	{
		bool operator()(const T& x, const T&  y) const
		{
			return x < y;
		}
	};
	template<class T>
	struct greater
	{
		bool operator()(const T& x, const T&  y) const
		{
			return x > y;
		}
	};
	// 优先级队列 -- 大堆 < 小堆 >
	template<class T, class Container = vector<T>, class Compare = less<T>>
	class priority_queue
	{
	public:
		void AdjustUp(int child)
		{
			Compare comFunc;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				//if (_con[parent] < _con[child])
				if (comFunc(_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 AdjustDown(int parent)
		{
			Compare comFunc;
			size_t child = parent * 2 + 1;
			while (child < _con.size())
			{
				//if (child+1 < _con.size() && _con[child] < _con[child+1])
				if (child + 1 < _con.size() && comFunc(_con[child], _con[child + 1]))
				{
					++child;
				}
				//if (_con[parent] < _con[child])
				if (comFunc(_con[parent], _con[child]))
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
		void pop()
		{
			assert(!_con.empty());
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			AdjustDown(0);
		}
		const T& top()
		{
			return _con[0];
		}
		size_t size()
		{
			return _con.size();
		}
		bool empty()
		{
			return _con.empty();
		}
	private:
		Container _con;
	};

代码解释:

  • 这里模拟实现底层容器缺省参数使用vector,比较规则使用Less。
  • 这里的比较方式均是自己实现的仿函数。

四、容器适配器

4.1、什么是适配器

适配器是一种设计模式(设计模式是一套被反复使用的,多数人知晓的,经过分类编目的,代码设计经验的总结),该模式是讲一个类的接口转换成客户希望的另一个接口。

4.2、适配模式

  • 在计算机编程中,适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。
  • 适配器模式主要应用于,当接口里定义的方法无法满足客户的需求,或者说接口里定义的方法的名称或者方法界面与客户需求有冲突的情况。
  • 两类模式:对象适配器模式 - 在这种适配器模式中,适配器容纳一个它我包裹的类的实例。在这种情况下,适配器调用被包裹对象的物理实体。类适配器模式 - 这种适配器模式下,适配器继承自已实现的类(一般多重继承)。
  • 在计算机编程中,适配器包括:容器适配器、迭代器适配器、泛函适配器等。

4.3、STL标准库中stack和queue的底层结构

虽然stack和queue也可以存放元素,但是在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为栈和队列只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque,比如:

当然了,这里的容器都是默认容器,容器不唯一,我们可以显式传对应的容器。

到此这篇关于C++深入刨析优先级队列priority_queue的使用的文章就介绍到这了,更多相关C++ priority_queue内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++ 中"priority_queue" 优先级队列实例详解

    C++ 中"priority_queue" 优先级队列实例详解 1. 简介 标准库队列使用了先进先出(FIFO)的存储和检索策略. 进入队列的对象被放置在尾部, 下一个被取出的元素则取自队列的首部. 标准库提供了两种风格的队列: FIFO 队列(FIFO queue, 简称 queue), 以及优先级队列(priority queue). priority_queue 允许用户为队列中存储的元素设置优先级. 这种队列不是直接将新元素放置在队列尾部, 而是放在比它优先级低的元素前面. 标

  • C++ STL priority_queue自定义排序实现方法详解

    前面讲解 priority_queue 容器适配器时,还遗留一个问题,即当 <function> 头文件提供的排序方式(std::less<T> 和 std::greater<T>)不再适用时,如何自定义一个满足需求的排序规则. 首先,无论 priority_queue 中存储的是基础数据类型(int.double 等),还是 string 类对象或者自定义的类对象,都可以使用函数对象的方式自定义排序规则.例如: #include<iostream> #in

  • 详解c++优先队列priority_queue的用法

    既然是队列那么先要包含头文件#include <queue>, 他和queue不同的就在于我们可以自定义其中数据的优先级, 让优先级高的排在队列前面,优先出队 优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的 和队列基本操作相同: top 访问队头元素 empty 队列是否为空 size 返回队列内元素个数 push 插入元素到队尾 (并排序) emplace 原地构造一个元素并插入队列 pop 弹出队头元素 swap 交换内容 定义:prio

  • C++中priority_queue模拟实现的代码示例

    目录 priority_queue概述 priority_queue定义 priority_queue特点 构造函数 修改相关函数 push pop 容量相关函数 size empty 元素访问相关函数 top 总结 priority_queue概述 priority_queue定义 优先级队列是不同于先进先出队列的另一种队列.每次从队列中取出的是具有最高优先权的元素. priority_queue特点 优先队列是一种容器适配器,首先要包含头文件 #include<queue>, 他和queu

  • c++优先队列(priority_queue)用法详解

    普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除. 在优先队列中,元素被赋予优先级.当访问元素时,具有最高优先级的元素最先删除.优先队列具有最高级先出 (first in, largest out)的行为特征. 首先要包含头文件#include<queue>, 他和queue不同的就在于我们可以自定义其中数据的优先级, 让优先级高的排在队列前面,优先出队. 优先队列具有队列的所有特性,包括队列的基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的. 和队列基本

  • C++ 容器适配器priority_queue的使用及实现代码

    优先级队列(Priority Queue) 队列是一种特征为FIFO的数据结构,每次从队列中取出的是最早加入队列中的元素.但是,许多应用需要另一种队列,每次从队列中取出的应是具有最高优先权的元素,这种队列就是优先级队列(Priority Queue),也称为优先权队列. 1. 优先级队列的概念 优先级队列的定义 优先级队列是不同于先进先出队列的另一种队列.每次从队列中取出的是具有最高优先权的元素. 优先级队列的特点 优先级队列是0个或多个元素的集合,每个元素都有一个优先权或值. 当给每个元素分配

  • C++中priority_queue的使用与模拟实现

    目录 priority_queue的使用 priority_queue简介 priority_queue的使用 priority_queue的模拟实现 priority_queue的使用 priority_queue文档介绍 priority_queue简介 优先队列是一种容器适配器,有严格的排序标准,它的第一个元素总是它所包含的元素中最大的(或最小的). 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆(或 最小堆)元素(优先队列中位于顶部的元素). 默认情况下,如果没有为特定的p

  • C++深入刨析优先级队列priority_queue的使用

    目录 一.priority_queue的介绍 二.priority_queue的使用 三.priority_queue的模拟实现 四.容器适配器 4.1.什么是适配器 4.2.适配模式 4.3.STL标准库中stack和queue的底层结构 一.priority_queue的介绍 priority_queue官方文档介绍 翻译: 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中

  • C++ 中"priority_queue" 优先级队列实例详解

    C++ 中"priority_queue" 优先级队列实例详解 1. 简介 标准库队列使用了先进先出(FIFO)的存储和检索策略. 进入队列的对象被放置在尾部, 下一个被取出的元素则取自队列的首部. 标准库提供了两种风格的队列: FIFO 队列(FIFO queue, 简称 queue), 以及优先级队列(priority queue). priority_queue 允许用户为队列中存储的元素设置优先级. 这种队列不是直接将新元素放置在队列尾部, 而是放在比它优先级低的元素前面. 标

  • JavaScript 关于事件循环机制的刨析

    目录 前言: 一.事件循环和任务队列产生的原因: 二.事件循环机制: 三.任务队列: 3.1 任务队列的类型: 3.2 两者区别: 3.3 更细致的事件循环过程 四.强大的异步专家 process.nextTick() 4.1 process.nextTick()在何时调用? 前言: 这次主要整理一下自己对 Js事件循环机制,同步,异步任务,宏任务,微任务的理解,大概率暂时还有些偏差或者错误.如果有,十分欢迎各位纠正我的错误! 一.事件循环和任务队列产生的原因: 首先,JS是单线程,这样设计也是

  • Android广播事件流程与广播ANR原理深入刨析

    目录 序言 一.基本流程和概念 二.无序广播流程 注册广播接收者流程 广播通知流程 三.有序广播流程 四.广播ANR流程 五.总结 六.扩展问题 序言 本想写广播流程中ANR是如何触发的,但是如果想讲清楚ANR的流程,就不得不提整个广播事件的流程,所以就把两块内容合并在一起讲了. 本文会讲内容如下: 1.动态注册广播的整个分发流程,从广播发出,一直到广播注册者接收. 2.广播类型ANR的判断流程和原理. PS:本文基于android13的源码进行讲解. 一.基本流程和概念 动态广播的流程其实是很

  • Java集合框架之List ArrayList LinkedList使用详解刨析

    目录 1. List 1.1 List 的常见方法 1.2 代码示例 2. ArrayList 2.1 介绍 2.2 ArrayList 的构造方法 2.3 ArrayList 底层数组的大小 3. LinkedList 3.1 介绍 3.2 LinkedList 的构造方法 4. 练习题 5. 扑克牌小游戏 1. List 1.1 List 的常见方法 方法 描述 boolean add(E e) 尾插 e void add(int index, E element) 将 e 插入到 inde

  • Java集合框架之Stack Queue Deque使用详解刨析

    目录 1. Stack 1.1 介绍 1.2 常见方法 2. Queue 2.1 介绍 2.2 常见方法 3. Deque 3.1 介绍 3.2 常见方法 1. Stack 1.1 介绍 Stack 栈是 Vector 的一个子类,它实现了一个标准的后进先出的栈.它的底层是一个数组. 堆栈只定义了默认构造函数,用来创建一个空栈.堆栈除了包括由 Vector 定义的所有方法,也定义了自己的一些方法. 1.2 常见方法 方法 描述 E push(E item) 压栈 E pop() 出栈 E pee

  • Java源码刨析之ArrayQueue

    目录 ArrayQueue内部实现 ArrayQueue源码剖析 总结 ArrayQueue内部实现 在谈ArrayQueue的内部实现之前我们先来看一个ArrayQueue的使用例子: public void testQueue() { ArrayQueue<Integer> queue = new ArrayQueue<>(10); queue.add(1); queue.add(2); queue.add(3); queue.add(4); System.out.printl

  • Java源码刨析之ArrayDeque

    目录 前言 双端队列整体分析 数组实现ArrayDeque(双端队列)的原理 底层数据遍历顺序和逻辑顺序 ArrayDeque类关键字段分析 ArrayDeque构造函数分析 ArrayDeque关键函数分析 addLast函数分析 addFirst函数分析 doubleCapacity函数分析 pollLast和pollFirst函数分析 总结 前言 在本篇文章当中主要跟大家介绍JDK给我们提供的一种用数组实现的双端队列,在之前的文章LinkedList源码剖析当中我们已经介绍了一种双端队列,

  • Android Dispatchers.IO线程池深入刨析

    目录 一. Dispatchers.IO 1.Dispatchers.IO 2.DefaultScheduler类 3.LimitingDispatcher类 4.ExperimentalCoroutineDispatcher类 二.CoroutineScheduler类 1.CoroutineScheduler类的继承关系 2.CoroutineScheduler类的全局变量 三.Worker类与WorkerState类 1.WorkerState类 2.Worker类的继承关系与全局变量 3

随机推荐