【C++】priority_queue和仿函数

目录

1. priority_queue的介绍和使用

1.1 priority_queue的介绍

1.2 priority_queue 的使用

2. 仿函数

2.1 什么是仿函数 ?

2.2 仿函数的优缺点

2.3 仿函数的作用

作为判别式示例:

 3. priority_queue 的模拟实现


1. priority_queue的介绍和使用

1.1 priority_queue的介绍

priority_queue文档介绍

  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  2. 类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
  3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
  4. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定priority_queue类实例化指定容器类,则使用vector。
  5. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数 make_heap、push_heap和pop_heap来自动完成此操作。
  6. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭 代器访问,并支持以下操作:

 

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

1.2 priority_queue 的使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。

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

注意: 默认情况下priority_queue 使用仿函数是less,所以默认建成的是大堆;如果我们想要建小堆,就要指定仿函数 greater ,该仿函数包含在头文件<functional>中,由于仿函数是第三个模板参数,所以想要传递它,就必须要先传递第二个模板参数,即适配容器。

 

 

void TestPriority_Queue()
{
	//没有给定模板参数,默认建大堆,仿函数为less
	priority_queue<int> pq1;
	pq1.push(3);
	pq1.push(6);
	pq1.push(1);
	pq1.push(8);
	pq1.push(4);
	while (!pq1.empty())
	{
		cout << pq1.top() << " ";
		pq1.pop();
	}
	cout << endl;

	//建小堆,仿函数为greater
	priority_queue<int, vector<int>, greater<int>> pq2;
	pq2.push(3);
	pq2.push(6);
	pq2.push(1);
	pq2.push(8);
	pq2.push(4);
	while (!pq2.empty())
	{
		cout << pq2.top() << " ";
		pq2.pop();
	}
	cout << endl;

}

 

2. 仿函数

2.1 什么是仿函数 ?

仿函数(functor)也叫做函数对象,就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。

如下:

class compare_less
{
public:
	bool operator()(int A, int B)const 
	{ 
		return A < B;
	}

};
class compare_greater
{
public:
	bool operator()(int A, int B)const
	{
		return A > B;
	}

};
void function_test()
{
	compare_less lessFunc;
	cout << lessFunc(1, 3) << endl;

	compare_greater greaterFunc;
	cout << greaterFunc(1, 3) << endl;
}

 可以看到 compare_less类和 compare_greater 类重载了 () 后,就可以像正常函数去使用了,就像真的函数一样,因此,他们被称为仿函数。

2.2 仿函数的优缺点

优点:

  1. 仿函数比函数指针的执行速度快,函数指针时通过地址调用,而仿函数是对运算符operator进行自定义来提高调用的效率。
  2. 仿函数比一般函数灵活,可以同时拥有两个不同的状态实体,一般函数不具备此种功能。
  3. 仿函数可以作为模板参数使用,因为每个仿函数都拥有自己的类型。

缺点:

  1. 需要单独实现一个类。
  2. 定义形式比较复杂。

2.3 仿函数的作用

仿函数通常有下面四个作用:

  1. 作为排序规则,在一些特殊情况下排序是不能直接使用运算符<或者>时,可以使用仿函数。
  2. 作为判别式使用,即返回值为bool类型。
  3. 同时拥有多种内部状态,比如返回一个值得同时并累加。
  4. 作为算法for_each的返回值使用。

作为判别式示例:

#include <iostream>
#include <algorithm>
#include <list>
using namespace std;
class RmNum
{
private:
	int m_num;
	int m_count;

public:
	RmNum(int n)
		:m_num(n)
		, m_count(0)
	{
	}

	bool operator()(int)
	{
		return ++m_count == m_num;
	}
};

// 打印
void myPrintf(list<int>& lt)
{
	list<int>::iterator it;
	for (it = lt.begin(); it != lt.end(); it++)
	{
		cout << *it << "  ";
	}
	cout << endl;
}

int main()
{
	cout << "----------------仿函数作为判别式使用--------------" << endl;
	cout << "----------------list中插入数值--------------" << endl;
	list<int> tmpList;
	for (int i = 0; i < 9; i++)
	{
		tmpList.emplace_back(i);
	}
	cout << "----------------打印list中插入的数值--------------" << endl;
	myPrintf(tmpList);

	cout << "----------------删除list中符合条件的数值--------------" << endl;
	list<int>::iterator pos;
	pos = remove_if(tmpList.begin(), tmpList.end(), RmNum(2));
	tmpList.erase(pos, tmpList.end());
	cout << "----------------打印删除后list中的数值--------------" << endl;
	myPrintf(tmpList);
	getchar();
}

运行结果:

 3. priority_queue 的模拟实现

priority_queue.h:

#pragma once
namespace my_pq
{
	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
	{
	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 adjust_up(size_t child)
		{
			Compare com;
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
				if (com(_con[child],_con[parent]))
				{
					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] < _con[child + 1])

				if (child + 1< _con.size() && com(_con[child +1] ,_con[child]))
				{
					++child;
				}
				//if(_con[child] > _con[parent])

				if (com(_con[child], _con[parent]))
				{
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
					break;
			}
		}
		void push(const T& x)
		{
			_con.push_back(x);
			adjust_up(_con.size() - 1);
		}
		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			adjust_down(0);
		}
		const T& top()const
		{
			return _con[0];
		}
		bool empty()const
		{
			return _con.empty();
		}
		size_t  size()const
		{
			return _con.size();
		}
	private:
		Container _con;
	};
}

Test.cpp:

#include<iostream>
#include<vector>
using namespace std;
#include"priority_queue.h"
int main()
{
	my_pq::priority_queue<int> pq;
	pq.push(2);
	pq.push(5);
	pq.push(3);
	pq.push(8);
	pq.push(4);
	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;

	my_pq::priority_queue<int,vector<int>,greater<int>> pq1;
	pq1.push(2);
	pq1.push(5);
	pq1.push(3);
	pq1.push(8);
	pq1.push(4);
	while (!pq1.empty())
	{
		cout << pq1.top() << " ";
		pq1.pop();
	}
	cout << endl;
	return 0;
}

运行结果:

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只睡不醒的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值