priority_queue类(仿函数实现C++)

        本篇将讲解 priority_queue 的实现,叫做优先级队列,是包含在 <queue> 头文件中的一种数据结构,虽然他叫做队列,但是该数据结构并不服从先进先出的特点,而是和我们的堆很相似。本篇中将多出一个仿函数的调用。目录如下:

目录

1. priority_queue 的介绍

2. priority_queue 的实现

3. 仿函数下的 priority_queue

4. All Code

1. priority_queue 的介绍

        优先级队列是一种容器适配器,按照严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。所以对于这样的一个数据结构其实非常类似于大堆。需要注意的是,第一个元素总是所包含元素中最大的,说明我们在插入元素和删除元素的时候,我们都需要将其

        优先级队列被实现为容器适配器(容器适配器就是将特定容器封装作为其底层容器类),也就是可以在优先级队列中存储各种各样类型的数据元素

        对于底层容器的要求,底层容器可以是任何标准容器的模板,也可以是其他特定设计的容器类,容器应该可以通过随机访问迭代器访问,并且支持一下操作:

        

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

        标准容器类 vector deque 满足这些要求,默认情况下,如果没有指定 priority_queue 类实例化容器类,使用 vector。

2. priority_queue 的实现

        在上文中也已经提到,关于优先级队列的性质,和堆非常像,所以我们实现优先级队列相当于实现一个堆,所以实现向上调整算法向下调整算法是优先级队列的难点。

        注:对于一下算法的实现,源自更加详细的文章:堆/堆排序(C/C++)-CSDN博客,以下讲解的默认为大堆,双亲结点大于孩子结点。

        我们先实现向上调整算法,对于向上调整算法,其主要作用是在元素进入优先级队列的时候,对优先级队列中的元素进行调整,使其保持优先级队列的属性,但新元素进入优先级队列的时候,我们需要将其与双亲结点相比较,若新入的结点大于双亲结点,那让新节点与双亲结点交换位置(默认是大堆),然后继续向上与双亲结点相比较,直到进入的结点的到达堆顶或者不大于双亲结点的时候,则退出,实现如下:

	void AdjustUp(size_t child) {
		size_t parent = (child - 1) / 2;
		while (child > 0) {
			// 先建立大堆
			if (_con[parent] < _con[child]) {
				std::swap(_con[parent], _con[child]);
				child = parent;
				parent = (child - 1) / 2;
			}
			else {
				break;
			}
		}
	}

        接着我们实现向下调整算法,对于向下调整算法的实现,其主要作用为:当我们想要弹出元素的时候,弹出的元素是整个序列中的第一个元素,也就是堆顶的元素,当弹出之后,我们需要重新找到一个堆顶元素,所以这个时候,我们需要将堆尾的元素放在堆顶,然后从堆顶还是和他们的孩子结点相比较,若孩子结点大于当前结点,则与孩子结点相交换,直至到叶子结点为止。实现如下:

	void AdjustDown(size_t parent) {
		size_t child = parent * 2 + 1;
		while (child < size()) {
			// 现在是建大堆,需要找到大的
			if (child + 1 < size() && _con[child] < _con[child + 1])
				child++;
			if (_con[parent] < _con[child]) {
				std::swap(_con[parent], _con[child]);
				parent = child;
				child = parent * 2 + 1;
			}
			else {
				break;
			}
		}
	}

        对于优先级队列的其他类函数的实现,其主要还是依靠本身提供的容器适配器,由于实现较为简单,直接给出代码:

	// 构造函数
    priority_queue(const Container con = Container())
		:_con(con)
	{}

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

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

	const T& top() const {
		return _con.front();
	}

	void swap(priority_queue& pq) {
		_con.swap(pq._con);
	}

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


	void pop() {
		std::swap(_con[0], _con[size() - 1]);
		_con.pop_back();
		AdjustDown((int)0);
	}

3. 仿函数下的 priority_queue

        对于优先级队列来说,上文给出的也只是大堆优先级队列,但是若我们想要实现小堆的优先级队列呢?肯定还是需要依靠我们的模板,我们可以使用模板来构造大堆和小堆。

        这个模板的实现主要依靠仿函数,也就是我们重新写出一个类,让这个类在对应函数中实例化一个对象,然后让该对象调用对应的重载比较函数,用在我们的向上调整算法和向下调整算法中,因为只需要将算法中的不等号更改方向,就可以决定大堆还是小堆。

        默认情况下,实现的为大堆。大堆传入的仿函数为小于仿函数,小堆传入的仿函数为大于仿函数。两个仿函数的实现如下:

	template<class T>
	class Greater {
	public:
		bool operator()(const T& x, const T& y) {
			return x > y;
		}
	};

	template<class T>
	class Less {
	public:
		bool operator()(const T& x, const T& y) {
			return x < y;
		}
	};

        在向上调整算法和向下调整算法中的使用:

	void AdjustUp(size_t child) {
		size_t parent = (child - 1) / 2;
		Compare com;
		bool either = com(_con[parent], _con[child]);
		while (child > 0) {
			// 先建立大堆
			//if (_con[parent] < _con[child]) 
			if(com(_con[parent], _con[child])){
				std::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 < size()) {
			// 现在是建大堆,需要找到大的
			if (child + 1 < size() && com(_con[child], _con[child + 1]))
				child++;
			if (com(_con[parent], _con[child])) {
				std::swap(_con[parent], _con[child]);
				parent = child;
				child = parent * 2 + 1;
			}
			else {
				break;
			}
		}
	}

        如上,我们只要在函数中实例化一个对象,然后调用该对象的重载比较函数即可。若还存在异或,可以看看下面的完整代码。

4. All Code

namespace MyPriorityQueue {

	template<class T>
	class Greater {
	public:
		bool operator()(const T& x, const T& y) {
			return x > y;
		}
	};

	template<class T>
	class Less {
	public:
		bool operator()(const T& x, const T& y) {
			return x < y;
		}
	};

	template<class T, class Container = deque<T>, class Compare = Less<T>>
	class priority_queue {
	public:
		priority_queue(const Container con = Container())
			:_con(con)
		{}

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

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

		const T& top() const {
			return _con.front();
		}

		void swap(priority_queue& pq) {
			_con.swap(pq._con);
		}

		// 向上调整算法
		void AdjustUp(size_t child) {
			size_t parent = (child - 1) / 2;
			Compare com;
			bool either = com(_con[parent], _con[child]);
			while (child > 0) {
				// 先建立大堆
				//if (_con[parent] < _con[child]) 
				if(com(_con[parent], _con[child])){
					std::swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else {
					break;
				}
			}
		}

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

		void AdjustDown(size_t parent) {
			Compare com;
			size_t child = parent * 2 + 1;
			while (child < size()) {
				// 现在是建大堆,需要找到大的
				if (child + 1 < size() && com(_con[child], _con[child + 1]))
					child++;
				if (com(_con[parent], _con[child])) {
					std::swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else {
					break;
				}
			}
		}

		void pop() {
			std::swap(_con[0], _con[size() - 1]);
			_con.pop_back();
			AdjustDown((int)0);
		}

	private:
		Container _con;
	};
}
  • 54
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值