STL容器适配器 -- priority_queue(使用+实现)(C++)

priority_queue

  • priority_queue 简单介绍
  • priority_queue 使用
    • 内置类型测试
    • 自定义类型测试
  • priority_queue 模拟实现
  • 仿函数(less、greater)

priority_queue 简单介绍

优先级队列是一种容器适配器。类似于堆,可以随时插入元素,只能检索优先级队列中位于顶部的元素。

priority_queue是作为容器适配器实现的,容器适配器是对特定容器类封装,作为其底层的容器。vector、deque符合priority_queue需求,如果没有指定特定的底层容器,默认使用vector。需要支持随机访问迭代器,以便内部保持堆结构。

priority_queue 使用

在默认底层容器vector中使用堆算法,将vector中的元素构成堆的结构,所以priority_queue就是堆
使用场景:在需要用到堆的位置。priority_queue默认是大堆。

函数 接口说明
priority_queue() 构造空的优先级队列
priority_queue(first, last) 迭代器范围构造优先级队列
empty() 判断优先级队列是否为空
top() 返回堆顶元素的const引用
push(val) 将val插入优先级队列中
pop() 删除堆顶元素

内置类型测试

void test_priority_queue()
{
	//默认是大堆 -- less   less在sort中对应的是升序,但是在priority_queue中建的大堆
	//priority_queue pq;

	//仿函数greater控制建小堆
	priority_queue<int, vector<int>, greater<int>> pq;
	pq.push(5);
	pq.push(10);
	pq.push(3);
	pq.push(1);

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

test2:

void test_priority_queue()
{
	vector<int> v{ 3,2,7,6,0,4,1,9,8,5 };

	//默认大堆
	priority_queue<int> pq;
	for (auto& e : v)
	{
		pq.push(e);
	}
	cout << pq.top() << endl;

	//创建小堆
	priority_queue<int, vector<int>, greater<int>> pq1(v.begin(), v.end());
	cout << pq1.top() << endl;
}

自定义类型测试

如果是自定义类型的数据,用户需要提供大于(>)或者小于(<)的重载

class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);

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);
	}

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_queue()
{
	// 大堆,需要用户在自定义类型中提供<的重载
	priority_queue<Date> pq1;
	pq1.push(Date(2023, 8, 6));
	pq1.push(Date(2023, 7, 6));
	pq1.push(Date(2023, 6, 6));
	cout << pq1.top() << endl;

	// 如果要创建小堆,需要用户提供>的重载
	priority_queue<Date, vector<Date>, greater<Date>> pq2;
	pq2.push(Date(2023, 8, 6));
	pq2.push(Date(2023, 7, 6));
	pq2.push(Date(2023, 6, 6));
	cout << pq2.top() << endl;
}

注意: 如果自定义类型没有重载用于比较的符号, 则程序运行错误。

priority_queue 模拟实现

priority_queue类模板实现,借用了一个容器vector和两个仿函数less、greater。如果想进一步观察priority_queue的逻辑结构,请点击该链接:堆的实现

#include 
#include 

// priority_queue--->堆
namespace kpl
{
	template<class T>
	struct less
	{
		bool operator()(const T& left, const T& right)
		{
			return left < right;
		}
	};

	template<class T>
	struct greater
	{
		bool operator()(const T& left, const T& right)
		{
			return left > right;
		}
	};

	template<class T, class Container = std::vector<T>, class Compare = less<T>>
	class priority_queue
	{
	public:
		// 创造空的优先级队列
		priority_queue()
		{}
		
		/*template
		priority_queue(InputIterator first, InputIterator last)
		{	//这里可以不使用初始化列表
			//数据存入
			while (first != last)
			{
				_con.push_back(*first);
				++first;
			}

			//向下调整建堆
			for (size_t i = (_con.size() - 1 - 1) / 2; i >= 0; --i)
			{
				AdjustDown(i);
			}
		}*/
		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
			: _con(first, last)
		{
			// 将_con中的元素调整成堆的结构
			//向下调整建堆
			for (size_t i = (_con.size() - 1 - 1) / 2; i >= 0; --i)
			{
				AdjustDown(i);
			}
		}

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

		void pop()
		{
			assert(!_con.empty());

			swap(_con.front(), _con.back());
			_con.pop_back();
			AdjustDown(0);
		}

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

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

		// 堆顶元素不允许修改,因为:堆顶元素修改可以会破坏堆的特性
		const T& top()const
		{
			return _con.front();
		}
	private:
		// 向上调整
		void AdjustUP(int child)
		{
			int parent = (child - 1) / 2;
			while (child)
			{
				if (_com(_con[parent], _con[child]))
				{
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

		// 向下调整
		void AdjustDown(int parent)
		{
			size_t child = parent * 2 + 1;
			while (child < _con.size())
			{
				// 找以parent为根的较大的孩子
				if (child + 1 < _con.size() && _com(_con[child], _con[child + 1]))
					child += 1;

				// 检测双亲是否满足情况
				if (_com(_con[parent], _con[child]))
				{
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
	private:
		Container _con;
		Compare _com;
	};
}

仿函数(less、greater)

仿函数(functor)是一个能行使函数功能的类或结构体。它实际上是一个可调用的对象,可以像调用函数一样来使用它,传入参数并获得返回值。与普通函数不同的是,仿函数可以保存状态信息,并且可以被其他函数调用。为了成为一个仿函数,类或结构体必须重载 operator() 运算符。在C++中,仿函数通常用于STL算法中的自定义排序、查找、过滤等操作。

test1:

//仿函数/函数对象
template<class T>
struct less
{
	bool operator()(const T& left, const T& right)
	{
		return left < right;
	}
};

template<class T>
struct greater
{
	bool operator()(const T& left, const T& right)
	{
		return left > right;
	}
};

int main()
{
	less<int> lessFunc;
	cout << lessFunc(1, 2) << endl;
	cout << lessFunc.operator()(1, 2) << endl;
	cout << less<int>()(1, 2) << endl;
}

test2: 仿函数的匿名对象使用

int main()
{
	greater<int> g;

	vector<int> v{ 1,3,4,1,0 };
	sort(v.begin(), v.end(), g);
	//sort(v.begin(), v.end(), greater());
	//greater() --> 匿名对象
	
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	cout << greater<int>()(1, 2) << endl;
	cout << g(1, 2) << endl;
}

你可能感兴趣的:(c++,STL,容器适配器,priority_queue,仿函数)