priority_queue
- 1. 优先级队列的使用
 - 2. priority_queue的模拟实现
 
- 2.1 size & empty & top
 - 2.2 仿函数
 
- 2.2.1 push & 向上调整算法
 - 2.2.2 pop & 向下调整算法
 
- 2.3 构造函数
 - 2.4 关于T是自定义类型
 
- 附:priority_queue.h
 
 优先级队列也是一种
容器适配器,默认情况下它适配的是vector,以支持
堆的算法中频繁的随机访问。priority_queue不像stack & queue的适配只是简单复用,还搭配了堆的算法。那么,如何控制大堆还是小堆呢?就要通过简单的
仿函数啦。let’s go!
正文开始
反爬链接
1. 优先级队列的使用
头文件:<queue>

- Container:默认情况下,它适配的是vector。理论上,底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类,但是必须支持随机访问迭代器访问,以及一系列基本接口。
 - Compare:默认情况下,大的优先级高(即默认是大堆),仿函数给的是
less(这确实有点奇怪)。小堆需要传入仿函数类型,它的头文件在<functional>中。 
#include<iostream>
#include<queue>
#include<functional> //greater算法头文件
using namespace std;
int main()
{
	//priority_queue<int> pq; //默认大的优先级高,仿函数为less
	priority_queue<int,vector<int>,greater<int>> pq;
	pq.push(1);
	pq.push(3);
	pq.push(5);
	pq.push(2);
	pq.push(4);
	
    // 适配容器都不能迭代器遍历,因为这样不能保证堆的性质。
	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;
	return 0;
}来一道小题:215. 数组中的第K个最大元素 - 力扣(LeetCode) (leetcode-cn.com)
💜 方法一:排序
时间复杂度:O(N*logN)
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        sort(nums.begin(),nums.end());
        return nums[nums.size()-k]; //倒数第k个
    }
};或排升序
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        sort(nums.begin(),nums.end(),greater<int>());//注意,传的是匿名 对象
        return nums[k-1];
    }
};💜 方法二:建大堆(堆排) + pop (k-1)次取堆顶
时间复杂度:O(N+k*logN)
空间复杂度:O(N) 😱
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int> maxHeap(nums.begin(), nums.end());
        while(--k)
        {
            maxHeap.pop();
        }
        return maxHeap.top();
    }
};- 注意:迭代器区间构造,即建堆。不需要遍历push了
 
如果N>>k,法1和法2的代价太大,我们借鉴topK问题的思路,建一个k个数的小堆 ——
💜 方法三:建一个k个数的小堆 + 取堆顶元素
时间复杂度:O(k+(N-k)*logk)
空间复杂度:O(k)
时间复杂度和法二相差不大,但空间消耗最小,是这道题目的极致写法。
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        //小堆
        priority_queue<int, vector<int>, greater<int>> minHeap(nums.begin(), nums.begin()+k);
        for(int i = k; i<nums.size(); i++)
        {
            if(nums[i]>minHeap.top())
            {
                minHeap.pop();
                minHeap.push(nums[i]);
            }
        }
        return minHeap.top();
    }
};2. priority_queue的模拟实现
priority_queue框架,它的底层是一个随机容器。模板参数的第三个参数(仿函数)后面详谈——
template<class T,class Container = vector<T>,class Compare = less<T>>
class priority_queue
{
private:
    void ajust_up(size_t child);
    void ajust_down(size_t parent);
public:
    priority_queue()
    {}
    void push(const T& x);
    void pop();
    const T& top() const;
    size_t size() const;
    bool empty() const;
private:
    Container _con;
};2.1 size & empty & top
一系列简单接口。top是取堆顶元素。
const T& top() const
    {
        return _con[0];
    }
    size_t size() const
    {
        return _con.size();
    }
    bool empty() const
    {
        return _con.empty();
    }2.2 仿函数
我们写一个类,没有任何成员变量,重载() ——
template<class T>
	struct Less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};
	template<class T>
	struct Greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};不同仿函数类型的对象,用()来调用对应的函数比较方式,就实现了像函数一样调用 ——
// Less<T> - 仿函数类型,less 仿函数对象
int main()
{
	Less<int> less;
	cout << less(1, 2) << endl;
	cout << less.operator()(1, 2) << endl; // 等价于
    
	cout << Less<int>()(1, 2) << endl; //匿名对象
	Greater<int> greater;
	cout << greater(1, 2) << endl;
	cout << greater.operator()(1, 2) << endl;// 等价于
    
	cout << Greater<int>()(1, 2) << endl; //匿名对象
	return 0;
}建大堆还是建小堆本质是由于ajust_up和ajust_down中的比较符号不同,那么就要通过仿函数来控制。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-72nz857y-1649750293911)(C:\Users\13136\AppData\Roaming\Typora\typora-user-images\image-20220412155308474.png)] 【C++】优先级队列 priority_queue的使用及模拟实现@STL —— 仿函数_算法_02](https://file.cfanz.cn/uploads/png/2023/04/14/1/X95fHI6U6c.png)
2.2.1 push & 向上调整算法
优先级队列的插入及删除,就是在堆的基础上做插入删除,堆的算法我们都相当熟悉了。
堆插 = 尾插 + 向上调整
void push(const T& x)
    {
        _con.push_back(x);
        ajust_up(_con.size() - 1);
    }💜 向上调整算法

void ajust_up(size_t child)
    {
        while (child > 0)
        {
            size_t parent = (child - 1) / 2;
            //if (_con[parent] < _con[child])
            if (Compare()(_con[parent], _con[child]))
            {
                swap(_con[parent], _con[child]);
                child = parent;
            }
            else
            {
                break;
            }
        }
    }2.2.2 pop & 向下调整算法
堆删 = 交换 + 再删(直接头删会破坏树的关系) + 向下调整(刚好符合左右都是大/小堆的条件)
void pop()
    {
        swap(_con[0], _con[_con.size() - 1]);
        _con.pop_back();
        ajust_down(0);
    }💜 向下调整算法

void ajust_down(size_t parent)
    {
        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() && Compare()(_con[child], _con[child + 1]))
                child++;
            //if (_con[parent],<_con[child])
            if (Compare()(_con[parent], _con[child]))
            {
                swap(_con[parent], _con[child]);
                parent = child;
                child = parent * 2 + 1;
            }
            else
            {
                break; 
            }
        }
    }向上调整和向下调整算法我们都把它实现成私有,因为不太希望别人直接调用。
2.3 构造函数
💜 迭代器区间构造:_con自定义类型会调用它的迭代器区间构造,不用再迭代器遍历+push了。但在这基础上,还需要构建成堆,为了使左右都是大(小)堆,要倒着建,从最后一个非叶子节点(即最后一个节点的父亲)开始向下调整。
public:
		priority_queue()
		{}
		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
			: _con(first,last)
		{
			//构建堆结构
			for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--)
			{
				ajust_down(i);
			}
		}2.4 关于T是自定义类型
我们考虑,当T是自定义类型(比如Date)时 ——
void test_priority_queue2()
	{
		//priority_queue<Date> pq;
		priority_queue<Date, vector<Date>, greater<Date>> pq;
		pq.push(Date(2022, 3, 26));
		pq.push(Date(2022, 4, 1));
		pq.push(Date(2022, 2, 19));
		pq.push(Date(2022, 4, 10));
		while (!pq.empty())
		{
			cout << pq.top() << endl;
			pq.pop();
		}
	}在仿函数中,<和>运算符是无法直接比较的。of course 我们可以重载运算符like this ——
class Date
	{
	public:
		Date(int year = 1900, int month = 1, int day = 1)
			: _year(year)
			, _month(month)
			, _day(day)
		{}
		bool operator<(const Date& d)const
		{
			return (_year < d._year) ||
				(_year == d._year && _month < d._month) ||
				(_year == d._year && _month == d._month && _day < d._day);
		}
		bool operator>(const Date& d)const
		{
			return (_year > d._year) ||
				(_year == d._year && _month > d._month) ||
				(_year == d._year && _month == d._month && _day > d._day);
		}
		friend ostream& operator<<(ostream& _cout, const Date& d);
	private:
		int _year;
		int _month;
		int _day;
	};
	ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}但是如果数据类型 不支持比较 或 比较的方式不是你想要的,可以自己实现仿函数,按照你想要的方式(Compare给我们留的口子)去控制比较逻辑,比如 ——
void test_priority_queue3()
	{
		//priority_queue<Date*> pq; //默认比较地址大小
		//priority_queue<Date*, vector<Date*>, lessPDate> pq;
		priority_queue<Date*, vector<Date*>, greaterPDate> pq;
		pq.push(new Date(2022, 3, 26));
		pq.push(new Date(2022, 4, 1));
		pq.push(new Date(2022, 2, 19));
		pq.push(new Date(2022, 4, 10));
		//默认比较地址大小,若想比较日期大小,自己写仿函数
		while (!pq.empty())
		{
			cout << *pq.top() << endl;
			pq.pop();
		}
	}于是我们自己写了仿函数,又因为私有成员类外无法访问,我们把这两个仿函数类声明为priority_queue的友元类 ——
struct lessPDate
	{
		bool operator()(const Date* d1,const Date* d2)
		{
			//return *d1 < *d2;
			return (d1->_year < d2->_year) ||
				(d1->_year == d2->_year && d1->_month < d2->_month) ||
				(d1->_year == d2->_year && d1->_month == d2->_month && d1->_day < d2->_day);
		}
	};
	struct greaterPDate
	{
		bool operator()(const Date* d1, const Date* d2)
		{
			//return *d1 > *d2;
			return (d1->_year > d2->_year) ||
				(d1->_year == d2->_year && d1->_month > d2->_month) ||
				(d1->_year == d2->_year && d1->_month == d2->_month && d1->_day > d2->_day);
		}
	};附:priority_queue.h
如果你被东一块儿西一块儿的代码搞晕了哈哈,欢迎通读.h文件 ——
#pragma once
#include<iostream>
#include<vector>
using namespace std;
namespace beatles
{
	template<class T>
	struct less
	{ 
		// 大堆
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};
	template<class T>
	struct greater
	{
		// 小堆
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};
	template<class T,class Container = vector<T>,class Compare = less<T>>
	class priority_queue
	{
	private:
		void ajust_up(size_t child)
		{
			while (child > 0)
			{
				size_t parent = (child - 1) / 2;
				//if (_con[parent] < _con[child]) //默认大堆的情况下
				if (Compare()(_con[parent], _con[child]))
				{
					swap(_con[parent], _con[child]);
					child = parent;
				}
				else
				{
					break;
				}
			}
		}
		void ajust_down(size_t parent)
		{
			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() && Compare()(_con[child], _con[child + 1]))
					child++;
				//if (_con[parent],<_con[child]) //默认大堆的情况下
				if (Compare()(_con[parent], _con[child]))
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break; 
				}
			}
		}
	public:
		priority_queue()
		{}
		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
			: _con(first,last)
		{
			//构建堆结构
			for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--)
			{
				ajust_down(i);
			}
		}
		void push(const T& x)
		{
			_con.push_back(x);
			ajust_up(_con.size() - 1);
		}
		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			ajust_down(0);
		}
		const T& top() const
		{
			return _con[0];
		}
		size_t size() const
		{
			return _con.size();
		}
		bool empty() const
		{
			return _con.empty();
		}
	private:
		Container _con;
	};
	// 测试尾插尾删,同时测试了向上调整&向下调整算法
	void test_priority_queue1()
	{
		//priority_queue<int> pq; //默认大的优先级高,仿函数为less
		priority_queue<int, vector<int>, greater<int>> pq;
		pq.push(1);
		pq.push(3);
		pq.push(5);
		pq.push(2);
		pq.push(4);
		while (!pq.empty())
		{
			cout << pq.top() << " ";
			pq.pop();
		}
		cout << endl;
	}
	class Date
	{
		friend struct lessPDate;
		friend struct greaterPDate;
	public:
		Date(int year = 1900, int month = 1, int day = 1)
			: _year(year)
			, _month(month)
			, _day(day)
		{}
		bool operator<(const Date& d)const
		{
			return (_year < d._year) ||
				(_year == d._year && _month < d._month) ||
				(_year == d._year && _month == d._month && _day < d._day);
		}
		bool operator>(const Date& d)const
		{
			return (_year > d._year) ||
				(_year == d._year && _month > d._month) ||
				(_year == d._year && _month == d._month && _day > d._day);
		}
		friend ostream& operator<<(ostream& _cout, const Date& d);
	private:
		int _year;
		int _month;
		int _day;
	};
	ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}
	// 对于自定义类型,可以重载>和<
	// 如果数据类型不支持比较 或 比较的方式不是你想要的
	// 可以自己实现 仿函数,按照你想要的方式去控制比较逻辑
	void test_priority_queue2()
	{
		//priority_queue<Date> pq;
		priority_queue<Date, vector<Date>, greater<Date>> pq;
		pq.push(Date(2022, 3, 26));
		pq.push(Date(2022, 4, 1));
		pq.push(Date(2022, 2, 19));
		pq.push(Date(2022, 4, 10));
		while (!pq.empty())
		{
			cout << pq.top() << endl;
			pq.pop();
		}
	}
	struct lessPDate
	{
		bool operator()(const Date* d1,const Date* d2)
		{
			//return *d1 < *d2;
			return (d1->_year < d2->_year) ||
				(d1->_year == d2->_year && d1->_month < d2->_month) ||
				(d1->_year == d2->_year && d1->_month == d2->_month && d1->_day < d2->_day);
		}
	};
	struct greaterPDate
	{
		bool operator()(const Date* d1, const Date* d2)
		{
			//return *d1 > *d2;
			return (d1->_year > d2->_year) ||
				(d1->_year == d2->_year && d1->_month > d2->_month) ||
				(d1->_year == d2->_year && d1->_month == d2->_month && d1->_day > d2->_day);
		}
	};
	void test_priority_queue3()
	{
		//priority_queue<Date*> pq; //默认比较地址大小
		//priority_queue<Date*, vector<Date*>, lessPDate> pq;
		priority_queue<Date*, vector<Date*>, greaterPDate> pq;
		pq.push(new Date(2022, 3, 26));
		pq.push(new Date(2022, 4, 1));
		pq.push(new Date(2022, 2, 19));
		pq.push(new Date(2022, 4, 10));
		//默认比较地址大小,若想比较日期大小,自己写仿函数
		while (!pq.empty())
		{
			cout << *pq.top() << endl;
			pq.pop();
		}
	}
    
}持续更新@边通书
                










