【STL详解 —— priority_queue的使用与模拟实现】

这篇具有很好参考价值的文章主要介绍了【STL详解 —— priority_queue的使用与模拟实现】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

priority_queue的使用

priority_queue的介绍

【STL详解 —— priority_queue的使用与模拟实现】,C++,c++,算法,开发语言

std::priority_queue 是 C++ 标准库中的容器适配器,它提供了一种基于堆的优先级队列实现。优先级队列是一种特殊的队列,其中的元素按照一定的优先级顺序排列,而不是按照它们被插入的顺序。

std::priority_queue 中,插入元素时会根据元素的值自动进行排序,最大(或最小)的元素总是位于队列的顶部。对顶部元素的访问和弹出操作都是 O(1) 的时间复杂度,而插入操作则是 O(log n) 的时间复杂度。

priority_queue的定义方式

【STL详解 —— priority_queue的使用与模拟实现】,C++,c++,算法,开发语言
注意看 std::priority_queue的模板参数,总共三个参数

  1. T (Type):这是最重要的模板参数,它表示存储在优先队列中的元素类型。例如,如果要创建一个存储整数的优先队列,则可以指定 int 作为 T 的类型。

  2. Container:这是一个可选的模板参数,用于指定底层容器的类型,默认情况下是 std::vector。优先队列使用底层容器来存储元素,因此可以通过指定不同的容器类型来影响优先队列的性能和行为。

  3. Compare:这也是一个可选的模板参数,用于指定比较函数的类型,默认情况下是 std::less。比较函数用于确定优先队列中元素的顺序,例如 std::less 表示使用 < 运算符来进行比较,创建一个最大堆;而 std::greater 则表示使用 > 运算符来进行比较,创建一个最小堆。

因为后两个参数给了缺省值,所以在定义的时候可以只传一个参数,但注意,缺省只能从右往左缺,不能从左往右缺。

std::priority_queue<int> pq; // 创建一个存储整数的最大堆

std::priority_queue<int, std::deque<int>> pq; // 创建一个存储整数的最大堆,使用双端队列作为底层容器

std::priority_queue<int, std::vector<int>, std::greater<int>> pq; // 创建一个存储整数的最小堆

std::priority_queue<int, std::deque<int>, std::greater<int>> pq; // 创建一个存储整数的最小堆,使用双端队列作为底层容器,使用 std::greater 进行元素比较

priority_queue各个接口的使用

priority_queue的各个成员函数及其功能如下:

成员函数 功能
push 插入元素到队尾 (并排序)
pop 弹出队头元素 (堆顶元素)
top 访问队头元素 (堆顶元素)
size 获取队列中有效元素个数
empty 判断队列是否为空
swap 交换两个队列的内容

下面分别演示一个建最大堆和建最小堆的priority_queue。

#include <iostream>
#include <queue>

int main() {
    // 创建一个最大堆,默认使用 std::less 比较函数
    std::priority_queue<int> maxHeap;

    // 插入元素
    maxHeap.push(3);
    maxHeap.push(1);
    maxHeap.push(5);

    // 访问顶部元素
    std::cout << "Top of maxHeap: " << maxHeap.top() << std::endl; // 输出: 5

    // 弹出顶部元素
    maxHeap.pop();

    // 创建一个最小堆,使用 std::greater 比较函数
    std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;

    // 插入元素
    minHeap.push(3);
    minHeap.push(1);
    minHeap.push(5);

    // 访问顶部元素
    std::cout << "Top of minHeap: " << minHeap.top() << std::endl; // 输出: 1

    return 0;
}

priority_queue的模拟实现

仿函数

首先先了解一下仿函数:

仿函数(Functor) 是 C++ 中的一个概念,它是一种行为类似函数的对象,可以像函数一样被调用。仿函数实际上是一个类对象,重载了函数调用运算符 operator()

因为priority_queue是基于堆实现的,在堆排中我们每次的插入元素都需要进行向上调整,每次pop都需要向下调整,所以在下面的模拟实现中我们使用仿函数来进行改写我们之前写过的向上调整和向下调整。

示例仿函数

#include <iostream>

// 定义一个仿函数类
class MyFunctor {
public:
    // 重载函数调用运算符
    int operator()(int x, int y) {
        return x + y;
    }
};

int main() {
    MyFunctor myFunctor; // 创建一个仿函数对象

    // 使用仿函数对象进行函数调用
    int result = myFunctor(3, 4);

    std::cout << "Result: " << result << std::endl; // 输出: 7

    return 0;
}

priority_queue的模拟实现

我们在之前写过数据结构|堆及其堆排序,我们之前实现的AdjustUpAdjustDown 分别如下:

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}


void AdjustDown(HPDataType* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if ((a[child] < a[child + 1]) && child + 1 < size)
		{
			child++;
		}
		if (a[parent] < a[child])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

这里我们因为确定元素的类型是int,所以直接使用了 < >来进行比较大小。
下面我们使用仿函数改写:
因为仿函数是一个类对象,所以我们分别使用lessgreater 来表示不同功能的仿函数类。

template<class T>
	class less
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};

	template<class T>
	class greater
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

由参数可知:使用类模板 class Compare 仿函数比普通函数显得更加灵活,完美的诠释了泛型编程。
后期让想改变排序方式只用更改模板参数即可。

template<class T, class Container = std::vector<T>,class Compare = less<T>>
	void adjust_up(size_t child)
		{
			Compare com;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (com(_con[parent], _con[child]))
				{
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

	void adjust_down(size_t parent)
		{
			Compare com;
			size_t child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
				{
					++child;
				}
				if (com(_con[parent], _con[child]))
				{
					std::swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

示例:

//qq::priority_queue<int,vector<int>,greater<int>> pq;
	qq::priority_queue<int> pq;		//隐式定义,默认是less,建大堆
	pq.push(1);
	pq.push(3);
	pq.push(2);
	pq.push(8);
	pq.push(5);

	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	//8 5 3 2 1

显示调用,建小堆。

	qq::priority_queue<int,vector<int>,greater<int>> pq;
	//qq::priority_queue<int> pq;
	pq.push(1);
	pq.push(3);
	pq.push(2);
	pq.push(8);
	pq.push(5);

	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	//1 2 3 5 8

建立的是大堆(大顶堆),那么每次从堆中取出的都是当前堆中的最大值,再进行pop,向下调整。因此,如果你从大堆中依次取出所有元素并打印,那么打印出来的序列将是降序的。
同理 建立的是小堆(小顶堆),那么每次从堆中取出的都是当前堆中的最小值,再进行pop,向下调整。因此,如果你从小堆中依次取出所有元素并打印,那么打印出来的序列将是升序的。文章来源地址https://www.toymoban.com/news/detail-854156.html

namespace qq
{
	template<class T>
	class less
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};

	template<class T>
	class greater
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

	template<class T, class Container = std::vector<T>,class Compare = less<T>>
	class priority_queue
	{
	public:
		void adjust_up(size_t child)
		{
			Compare com;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (com(_con[parent], _con[child]))
				{
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

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

		void adjust_down(size_t parent)
		{
			Compare com;
			size_t child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
				{
					++child;
				}
				if (com(_con[parent], _con[child]))
				{
					std::swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

		void pop()
		{
			std::swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			adjust_down(0);
		}

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

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

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

到了这里,关于【STL详解 —— priority_queue的使用与模拟实现】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【STL】priority_queue(优先级队列)详解及仿函数使用(附完整源码)

    1. priority_queue介绍和使用 1.1 priority_queue介绍 优先级队列也是在 queue 里: 因此和 queue 一样, priority_queue 也是一个容器适配器。priority_queue官方文档 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。 类似于堆,在堆中可以随

    2024年02月08日
    浏览(32)
  • 【C++】priority_queue使用与模拟实现

    🚀 作者简介:一名在后端领域学习,并渴望能够学有所成的追梦人。 🚁 个人主页:不 良 🔥 系列专栏:🛸C++  🛹Linux 📕 学习格言:博观而约取,厚积而薄发 🌹 欢迎进来的小伙伴,如果小伙伴们在学习的过程中,发现有需要纠正的地方,烦请指正,希望能够与诸君一同

    2024年02月16日
    浏览(32)
  • 【C++】STL——容器适配器priority_queue(优先级队列)详解 及 仿函数的介绍和使用

    这篇文章我们接着上一篇的内容,再来学一个STL里的容器适配器—— priority_queue (优先级队列) 1.1 priority_queue的介绍 我们上一篇文章学了 queue (队列),那优先级队列也是在 queue 里面的: 和 queue 一样, priority_queue 也是一个容器适配器,那他和 queue 有什么区别呢?我们一

    2024年02月07日
    浏览(35)
  • 《priority_queue的模拟实现》

    本文主要介绍 所谓的仿函数(functor),是通过重载 () 运算符模拟函数形为的类。 因此,这里需要明确两点: 仿函数不是函数,它是个类; 仿函数重载了()运算符,使得它的对你可以像函数那样子调用(代码的形式好像是在调用函数)。(C语言中就是函数指针) 为了适应更多的

    2024年02月07日
    浏览(26)
  • 优先级队列priority_queue模拟实现

    🌟🌟hello,各位读者大大们你们好呀🌟🌟 🚀🚀系列专栏:【C++的学习】 📝📝本篇内容:C++容器优先级队列priority_queue模拟实现 ⬆⬆⬆⬆上一篇:string模拟实现 💖💖作者简介:轩情吖,请多多指教( •̀֊•́ ) ̖́- ①优先级队列是一种容器适配器,它的第一个元素总是

    2024年02月02日
    浏览(27)
  • 【C++】STL使用仿函数控制优先级队列priority_queue

    本文章讲解C++STL的容器适配器:priority_queue的实现,并实现仿函数控制priority_queue底层。 priority_queue叫做优先级队列,它的底层结构是堆,在库中,默认生成的是大堆 在库的实现中,使用vector作为该优先级队列的适配容器。 由于priority_queue也是一个适配器,所以它的接口函数

    2024年02月16日
    浏览(34)
  • C++:模版进阶 | Priority_queue的模拟实现

                                                创作不易,感谢三连支持  模板参数分类为 类型形参与非类型形参 。 类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。 非类型形参,就是 用一个常量作为类(函数)模板的一个参数 ,在类(函数

    2024年03月12日
    浏览(34)
  • [C++]priority_queue的介绍及模拟实现

    目录 priority_queue的介绍及模拟实现::                                                         priority_queue的介绍                                                         priority_queue的定义方式                 

    2024年02月04日
    浏览(34)
  • 【C++初阶】模拟实现优先级队列priority_queue

    👦个人主页:@Weraphael ✍🏻作者简介:目前学习C++和算法 ✈️专栏:C++航路 🐋 希望大家多多支持,咱一起进步!😁 如果文章对你有帮助的话 欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨ 优先级队列顾名思义就是 按优先级出队列 priority_queue 是一个容器适配器,默认使用

    2024年02月10日
    浏览(29)
  • C++ STL priority_queue

    目录 一.认识priority_queue 二. priority_queue的使用 三.仿函数  1.什么是仿函数  2.控制大小堆  3.TopK问题 四.模拟实现priority_queue  1.priority_queue的主要接口框架  2.堆的向上调整算法  3.堆的向下调整算法  4.仿函数控制大小堆  五.priority_queue模拟实现整体代码和测试 priority_queue-

    2024年02月07日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包