• C++ STL 【priority_queue】


    priority_queue的介绍和使用

    priority_queue的介绍

    原文priority_queue的介绍
    1、priority_queue是一个堆,它存储的容器的第一元素就是最大/最小值。
    2、优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部(堆顶弹出)。
    3、底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:

    成员函数函数作用
    empty()检测容器是否为空
    size()返回容器中有效元素个数
    front()返回容器中第一个元素的引用
    push_back()在容器尾部插入元素
    pop_back()删除容器尾部元素

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

    priority_queue的使用

    priority_queue通过Container容器存储,并且使用堆算法将容器造成逻辑上为对的结构,因此priority_queue就是堆。默认情况下priority_queue使用vector作为默认器,priority_queue为最大堆。

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

    priority_queue的主要接口:

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

    1、基本用法

    #include      // std::cout
    #include     // std::make_heap, std::pop_heap, std::push_heap, std::sort_heap
    #include        // std::vector
    #include
    #include
    #include
    using namespace std;
    int main() {
        int arr[] = {10,2,4,22,44,5,7,8,3,14};
    	// 默认情况下,创建的是大堆,其底层按照小于号比较
        priority_queue<int>q1(arr, arr + sizeof(arr)/sizeof(arr[0]));
    	//  通过传入泛函数greater 实现最小堆
        priority_queue<int,vector<int>,greater<int>>q2(arr, arr + sizeof(arr)/sizeof(arr[0]));
    	//  通过传入泛函数less 实现最大堆
    	priority_queue<int, vector<int>, less<int>>q3(arr, arr + sizeof(arr) / sizeof(arr[0]));
    	
    	 priority_queue<int> q4;
    	 for(auto &e:arr)
    	 {
    	 	q4.push(e);
    	 }
    
    	while (!q1.empty())
    	{
    		cout << q1.top() << " ";
    		q1.pop();
    	}
    	cout << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    2、如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供 “>” 或者"<" 运算符重载。

    class Date
    {
    	friend struct LessPDate;
    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 << endl;
    	return _cout;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    void test_priority_queue2()
    {
    	//priority_queue pq;
    	priority_queue<Date, vector<Date>, greater<Date>> pq;
    
    	pq.push(Date(2022, 3, 26));
    	pq.push(Date(2021, 10, 26));
    	pq.push(Date(2023, 3, 26));
    
    	while (!pq.empty())
    	{
    		cout << pq.top();
    		pq.pop();
    	}
    	cout << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3、如果数据类型,不支持比较,或者比较的方式,不是你想要的那么可以自己实现仿函数,按照自己想要的方式去比较,控制比较逻辑。

    struct LessPDate
    {
    	bool operator()(const Date* d1, const Date* d2) const
    	{
    		//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_queue2()
    {
    	priority_queue<Date*, vector<Date*>, LessPDate> pq;
    
    	pq.push(new Date(2022, 3, 26));
    	pq.push(new Date(2021, 10, 26));
    	pq.push(new Date(2023, 3, 26));
    
    	while (!pq.empty())
    	{
    		cout << *pq.top();
    		pq.pop();
    	}
    	cout << endl;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    仿函数

    仿函数(Functor)又称为函数对象(Function Object)是一个能行使函数功能的类。仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载operator()运算符。因为调用仿函数,实际上就是通过类对象调用重载后的operator()运算符。仿函数比函数要优秀。

    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;
    	}
    };
    
    int main()
    {
    	// Less->仿函数类型 less就叫函数对象
    	Less<int> lessi;
    	cout << lessi(1, 2) << endl;
    	cout << Less<int>()(1, 2) << endl;
    	cout << Less<double>()(1.1, 2.2) << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    priority_queue的模拟实现

    priority_queue的底层是用堆实现的,SLT源码用调用库函数的算法实现。我们主要学习如何实现priority_queue仿函数compare。

    namespace BBQ
    {
    
    	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
    	{
    	private:
    		// 向上调整
    		void adjust_up(size_t child)
    		{
    			Compare com;
    			size_t parent = (child - 1) / 2;
    			while (child > 0)
    			{
    				//if(_con[child] > _con[parent]) 等价于 if(_con[parent] < _con[child])
    				if (com(_con[parent], _con[child]))
    				{
    					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() && _con[child + 1] > _con[child])
    				if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
    				{
    					++child;
    				}
    
    				//if (_con[child] > _con[parent])
    				if (com(_con[parent], _con[child]))
    				{
    					swap(_con[child], _con[parent]);
    					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)
    			{
    				adjust_down(i);
    			}
    		}
    
    		void push(const T& x)
    		{
    			_con.push_back(x);
    			adjust_up(_con.size() - 1);
    		}
    
    		void pop()
    		{
    			//assert(!_con.empty());
    			swap(_con[0], _con[_con.size() - 1]);
    			_con.pop_back();
    			adjust_down(0);
    		}
    
    		const T& top()
    		{
    			return _con[0];
    		}
    
    		size_t size()
    		{
    			return _con.size();
    		}
    
    		bool empty()
    		{
    			return _con.empty();
    		}
    	private:
    		Container _con;
    	};
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
  • 相关阅读:
    IT项目经理必备的五种能力
    ECMAScript 6.0
    分式理想 & 对偶群 & 对偶空间
    设计模式:命令模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)
    类的成员之一:属性(field)
    代码安全审计规范
    【聚类】K-Means聚类
    vue3的那些事
    基于Python实现的五子棋游戏设计
    Redis核心数据结构实战与高性能解析
  • 原文地址:https://blog.csdn.net/weixin_58004346/article/details/126063400