[C/C++ -STL]仿函数及priority_queue深度剖析

一、priority_queue介绍及使用
1 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的使用
优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成
堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:
默认情况下priority_queue是大堆。

priority_queue()/priority_queue(first,
last)
构造一个空的优先级队列
empty( )
检测优先级队列是否为空,是返回true,否则返回
false
top( ) 返回优先级队列中最大(最小元素),即堆顶元素
push(x) 在优先级队列中插入元素x
pop() 删除优先级队列中最大(最小)元素,即堆顶元素

#include <vector>
#include <queue>
#include <functional> // greater算法的头文件
void TestPriorityQueue()
{
// 默认情况下,创建的是大堆,其底层按照小于号比较
vector<int> v{3,2,7,6,0,4,1,9,8,5};
priority_queue<int> q1;
for (auto& e : v)
q1.push(e);
cout << q1.top() << endl;
// 如果要创建小堆,将第三个模板参数换成greater比较方式
priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());
cout << q2.top() << endl;
}

二、priority_queue的模拟实现
1 priority_queue的向下、向上调整
priority_queue底层是大堆和小堆,要实现priority_queue就先实现队的向上调整和堆的向下调整
在这里插入图片描述

	//答对向上调整
		void Down(int parent) {

			size_t child = parent * 2+1;
			while (child<con.size()) {
				//左孩子小于右孩子
				if (child + 1 < con.size() && con[child] < con[child+1]) {
					++child;
				}

				 if (less(con[parent], con[child])) {
					 swap(con[child], con[parent]);
						parent = child;
					child = parent * 2+1;
				}
				else {
				
					break;
				}
			
			}
		}

在这里插入图片描述
在这里插入图片描述

//答对向上调整
		void Up(int child) {
			size_t parent = (child - 1) / 2;
			while (child > 0) {
				if (less(con[parent] , con[child])) {
					swap(con[child], con[parent]);
					child = parent;
					parent = (child - 1) / 2;

				}
				else {

					break;
				}

			}
		}

2 push()尾插
在这里插入图片描述
比如在9的位置插入35就必须采用向上调整的方式
在这里插入图片描述
将35和他的parent调换位置,调整为下边的图

在这里插入图片描述

3 pop()
在这里插入图片描述
将这个小堆调用pop()删除10()以后就必须向下调整
在这里插入图片描述
首先,将堆顶数据和最后一个数据调换数据,然后删除最后一个数据,最后得向下调整

在这里插入图片描述

最后调整为下面这样。
在这里插入图片描述
三、带有仿函数的priority_queue
仿函数(Functor)又称为函数对象(Function Object)是一个能行使函数功能的类。仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载 operator() 运算符。因为调用仿函数,实际上就是通过类对象调用重载后的 operator() 运算符。
如果编程者要将某种“操作”当做算法的参数,一般有两种方法:
一个办法就是先将该“操作”设计为一个函数,再将函数指针当做算法的一个参数。上面的实例就是该做法。
将该“操作”设计为一个仿函数(就语言层面而言是个 class),再以该仿函数产生一个对象,并以此对象作为算法的一个参数。
功能:
写一个简单类,除了维护类的基本成员函数外,只需要重载 operator() 运算符 。这样既可以免去对一些公共变量的维护,也可以使重复使用的代码独立出来,以便下次复用。而且相对于函数更优秀的性质,仿函数还可以进行依赖、组合与继承等,这样有利于资源的管理。
简言之:就是一个类,可以定义一些变量,省的使用全局变量,造成命名空间污染。

具体介绍篇

template <class T, class Container = vector,
class Compare = less< typename Container::value_type> > class priority_queue;

文档给出priority_queue的定义为这样,其中 class Compare = less为仿函数模板,用模板控制来priority_queue建成的是大堆的还是小堆。
仿函数其实是函数重载类型为为bool ,参数为两个模板参数,通过返回比较两个参数的大小的布尔值来控制大小堆的建成。

	template<class T>
	class min{
	public:
		bool operator() (T a, T b) {
			return a > b;
		}
	};
	template<class T>
	class max {
	public:
		bool operator() (T a, T b) {
			return a < b;
		}
	};

其中max是控制生成大堆,min控制生成小堆

附上全部代码:

template<class T>
	class less {
	public:
		bool operator() (T a, T b) {
			return a > b;
		}
	};
	template<class T>
	class max {
	public:
		bool operator() (T a, T b) {
			return a < b;
		}
	};
	template <class T, class Container = vector<T>,class Compare =max<T>>
	class st_priority_queue {
	private:
		Container con;
		Compare less;
	public:
		template<class Iterator>
		st_priority_queue(Iterator first, Iterator end) {
		
			while (first != end) {
				push(*first);
				++first;
			
			}
		}

		//答对向上调整
		void Up(int child) {
			size_t parent = (child - 1) / 2;
			while (child > 0) {
				if (less(con[parent] , con[child])) {
					swap(con[child], con[parent]);
					child = parent;
					parent = (child - 1) / 2;

				}
				else {

					break;
				}

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

		}
		void push(const T& x) {
			con.push_back(x);
			//大堆向上调整
			Up(con.size()-1);

		}
		bool empty() {
			return con.empty();
		}
		void pop() {
			swap(con[0], con[con.size() - 1]);
			con.pop_back();
			Down(0);

		}
		//答对向上调整
		void Down(int parent) {

			size_t child = parent * 2+1;
			while (child<con.size()) {
				//左孩子小于右孩子
				if (child + 1 < con.size() && con[child] < con[child+1]) {
					++child;
				}

				 if (less(con[parent], con[child])) {
					 swap(con[child], con[parent]);
						parent = child;
					child = parent * 2+1;
				}
				else {
				
					break;
				}
			
			}
		}

	};

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>