priority_queue的模拟实现

举报
跳动的bit 发表于 2022/08/30 07:23:20 2022/08/30
【摘要】 💦 priority_queue的模拟实现💨PriorityQueue.h#pragma once#include<vector>#include<list>#include<iostream>using namespace std;namespace bit{ //仿函数/函数对象——自定义类型,类型的对象可以像函数一样使用 template<class T> class Less ...

💦 priority_queue的模拟实现

💨PriorityQueue.h

#pragma once
#include<vector>
#include<list>
#include<iostream>
using namespace std;

namespace bit
{
	//仿函数/函数对象——自定义类型,类型的对象可以像函数一样使用
	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;
		}
	};

	//模板参数->类型
	//函数参数->对象

	//less->大堆 greater->小堆,默认是Less
	template<class T, class Container = vector<T>, class Compare = Less<T>>
	class priority_queue
	{
	public:
		priority_queue()
		{}
		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)//可以传任意类型的迭代器
		{
			//插入数据
			while(first != last)
			{
				_con.push_back(*first);
				++first;	
			}
			//建堆
			for(int i = (_con.size() - 1 - 1) / 2; i >= 0; --i)
			{
				AdjustDown(i);
			}
		}
		void AdjustUp(size_t child)
		{
			Compare com;
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
				//if (_con[parent] < _con[child])
				if (com(_con[parent], _con[child]))
				{
					swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else//父亲>=孩子
				{
					break;
				}
			}
		}
		void AdjustDown(size_t parent)
		{
			Compare com;
			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() && com(_con[child], _con[child + 1]))
				{
					++child;
				}
				//if (_con[parent] < _con[child])
				if (com(_con[parent], _con[child]))
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
		void push(const T& x)
		{
			_con.push_back(x);
			AdjustUp(_con.size() - 1);
		}
		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			AdjustDown(0);
		}
		const T& top() const 
		{
			return _con[0];
		}
		size_t size() 
		{
			return _con.size();
		}
		bool empty()
		{
			return _con.empty();
		}
	private:
		Container _con;//构造析构等可以不写,因为这是自定义类型
	};
	void test_priority_queue()
	{
		//priority_queue<int> pq;
		priority_queue<int, vector<int>, Greater<int>> pq;
		pq.push(1);
		pq.push(10);
		pq.push(11);
		pq.push(3);
		pq.push(5);
		pq.push(8);

		while (!pq.empty())
		{
			cout << pq.top() << " ";
			pq.pop();
		}
		cout << endl;

		list<int> lt = { 2, 5, 1, 3, 4 };
		priority_queue<int, vector<int>, greater<int>> pq1(lt.begin(), lt.end());
		while (!pq1.empty())
		{
			cout << pq1.top() << " ";
			pq1.pop();
		}
		cout << endl;  
	}
}

💨test.cpp

int main()
{
	bit::test_priority_queue();
	return 0;	
}

📝说明

  1. List item

    在以前我们要让大堆变小堆,都是直接去改符号,那如何能不改符号的情况下就能达到效果呢 ❓

    C语言其实可以利用函数指针来解决。但是 C++ 放弃用函数指针的方式,且非常不建议用函数指针,因为比较复杂。可以说 C++ 里的仿函数/函数对象就是为了替代 C语言里的函数指针。

    “ () ” 的功能可以提高优先级、强制类型转换、函数名 (形参表),仿函数用的是函数名 (形参表)。我们实现两个类,分别重载 “ () ” 完成比较大小功能,然后用 priority_queue 这个类来实例化控制。

在这里插入图片描述

在这里插入图片描述

  1. List item

    对比 priority_queue 的仿函数和 <algorithm> 中 sort 的仿函数 ❗

    priority_queue 是一个类,最好通过模板参数来传递,传的是类型。

    这里 less 中给的是 typename Container::value_type,并没有很特别的原因,less 里可以直接给 T,因为它是取 Container 中的 value_type,value_type 是被 typedef 的第一个模板参数,也就是 T。我们要去 Container 中取 value_type。Container 是一个模板,不知道它具体是啥,因为 vector 没有被实例化,所以去取可能会报错,所以加上 typename 是告诉编译器,后面是一个类型,当然报错与否,主要看编译器。这里后面遇到合适的场景会再谈。

在这里插入图片描述
sort 是一个函数,最好通过参数来传递。在这里插入图片描述
类模板是显示实例化的,所以说我们可以显示的指定参数;但是函数模板一般是通过实参去推演的,所以说 sort 得写在函数参数里。所以 priority_queue 是传类型,sort 是传对象。
在这里插入图片描述

💦 仿函数的变异玩法

要求往优先级队列里放一个日期类,且取出最大的日期 ❓

🧿 方案一:

#include<queue>
#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year = 2022, int month = 5, 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);	
	}
	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;	
}
int main()
{
	//我们可以放int,也可以放类
	priority_queue<Date> pq;
	pq.push(Date(2021, 11, 24));
	pq.push(Date(2021, 10, 24));
	pq.push(Date(2021, 12, 24));
	pq.push(Date(2022, 1, 24));

	//pq是自定义类型,所以上面需要重载<<,要取最大的日期,需要重载<
	cout << pq.top() << endl;
	pq.pop();
	return 0;
}

🧿 方案二:

假设极端情况,如果存的是日期类指针呢,如果再用上面的方式,取不出来,因为它比的是地址。解决方案就是我们自己实现仿函数来控制。

#include<queue>
#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year = 2022, int month = 5, 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);	
	}
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend class PDateLess;
private:
	int _year;
	int _month;
	int _day;   
};
ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day << endl;
	return _cout;	
}
class PDateLess
{
public:
	bool operator()(const Date* p1, const Date* p2)
	{
		/*if(p1->_year < p2->_year)
		   || (p1->_year == p2->_year && p1->_month < p2->_month)
		   || (p1->_year == p2->_year && p1->_month == p2->_month && p1->_day < p2->_day)
		{
			return true;	
		}
		else
		{
			return false;
		}*/
		//同上,重载了<的情况
		return *p1 < *p2;
	}
	
};
int main()
{
	priority_queue<Date*, vector<Date*>, PDateLess> pq;
	pq.push(new Date(2023, 11, 24));
	pq.push(new Date(2021, 10, 24));
	pq.push(new Date(2021, 12, 24));
	pq.push(new Date(2022, 1, 24));

	cout << (*pq.top()) << endl;
	pq.pop();
	return 0;
}

最后这里想说明的是我们可以通过仿函数来控制比较方式。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。