【C++容器】优先级队列 仿函数 反向迭代器

优先级队列

认识优先级队列

优先级队列(priority_queue)不是队列。优先级队列是一种容器适配器,与(stack),队列(queue)具有类似的功能。

先来了解一下优先级队列有哪些功能。看下图:

在这里插入图片描述

优先级队列底层是一个(默认是大堆),第二个参数默认给的是vector,不适合list,第三个参数是仿函数函数对象)。

在这里插入图片描述

模拟实现优先级队列

大部分成员函数可以通过调用vector的成员函数来实现。

#pragma once

namespace bit {
	//目前建堆默认大堆,如果想建小堆怎么办?
	template<class T, class Container = vector<T>>
	class priority_queue {
	private://不想让别人知道这两个函数,可以设置为私有
		void AdjustUp(int child)
		{
			//向上调整需要和兄弟比较吗?
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (_con[child] > _con[parent])
				{
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}
        //向下调整删除数据调用
		void AdjustDown(int parent)
		{
			Compare com;
			int child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && _con[child + 1] > _con[child])
				{
					++child;
				}
				if (com(_con[child], _con[parent]))
				{
					std::swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
	public:
		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
		{
			//传过来的迭代器可能不是有序的,所以要排序建堆。
			while (first != last)
			{
				push(*first);
				++first;
			}
		}
		priority_queue()//会调用vector的默认构造函数
		{}
		void push(const T& val)
		{
			_con.push_back(val);
			AdjustUp(_con.size() - 1);
		}
		const T& top() const
		{
			return _con[0];
		}
		bool empty() const
		{
			return _con.empty();
		}
		void pop()
		{
			//为什么swap不交换
			swap(_con[0], _con[_con.size() - 1]);
			//_con.size() - 1;//为什么这个程序不执行?执行了,但是size没变,只是_con.size() - 1
			_con.pop_back();
			AdjustDown(0);
		}
		void swap(priority_queue& pq)
		{
			std::swap(_con, pq._con);
		}
		size_t size() const
		{
			return _con.size();
		}
	private:
		Container _con;
	};
}

如果想建小堆,但不增加代码量的冗余,有办法吗?

浅谈仿函数

仿函数的大致了解

怎么建小堆?可以增加一个参数函数指针,这个函数来控制大堆还是小堆。但是函数指针声明比较复杂,有一些声明很长,导致通用性变差。所以,仿函数就来了。

写一个类,类中写相应的方法,这个类作为参数,那么就可以调用该类中的方法了。

仿函数(函数对象)不是函数调用,只是具有函数的功能。

如下代码是对上面代码的补充与修改

仿函数的实现

#pragma once

namespace bit {
	template<class T, class Container = vector<T>, class Compare = Greater<int>>
	class priority_queue {
	private://不想让别人知道这两个函数
		void AdjustUp(int child)
		{
			Compare com;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (com(_con[child], _con[parent]))
				{
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}
		void AdjustDown(int parent)
		{
			Compare com;
			int child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && com(_con[child + 1], _con[child]))
				{
					++child;
				}
				if (com(_con[child], _con[parent]))
				{
					std::swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
	public:
		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
		{
			//传过来的迭代器可能不是有序的,所以要排序建堆。
			while (first != last)
			{
				push(*first);
				++first;
			}
		}
		priority_queue()//会调用默认的构造函数
		{}
		void push(const T& val)
		{
			_con.push_back(val);
			AdjustUp(_con.size() - 1);
		}
		const T& top() const
		{
			return _con[0];
		}
		bool empty() const
		{
			return _con.empty();
		}
		void pop()
		{
			//为什么swap不交换
			swap(_con[0], _con[_con.size() - 1]);
			//_con.size() - 1;//为什么这个程序不执行?执行了,但是size没变,只是_con.size() - 1
			_con.pop_back();
			AdjustDown(0);
		}
		void swap(priority_queue& pq)
		{
			std::swap(_con, pq._con);
		}
		size_t size() const
		{
			return _con.size();
		}

	private:
		Container _con;
	};
}
template<class T>
struct Less
{
public:
	bool operator()(T& x, T& y)
	{
		return x > y;
	}
};
template<class T>
struct Greater
{
public:
	bool operator()(T& x, T& y)
	{
		return x < y;
	}
};

仿函数很方便,类中写许多的函数,资源的管理性不错,参数就可以只传一个类,封装性很强,灵活性很高。

反向迭代器

什么是反向迭代器?

**反向迭代器(reverse_iterator)**就是迭代器的相反。rbegin就是end,rend就是begin。

在这里插入图片描述

图中表示的rbegin,rend就是反向迭代器。

那如何实现反向迭代器呢?

反向迭代器的实现

仔细观察上面那张图,begin与rbegin,end与rend都是对称的。

·反向迭代器初始化可以调用正向迭代器的初始化

·反向迭代器++就是正向迭代器 的–,–就是正向迭代器的++。

先来看看库里面的反向迭代器是如何实现的。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

反向迭代器中有一个正向迭代器成员。++和–操作没有问题,都是调用正向迭代器的++和–函数,但是解引用(*)函数中为什么要–?

我们根据如下代码进行分析:

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

int main()
{
    vector<int> v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    v.push_back(5);
    vector<int>::reverse_iterator rit = v.rbegin();
    for (rit != v.rend())
    {
     	cout << *rit << " ";
        ++rit;
	}
    cout << endl;
}

在这里插入图片描述
)rbegin 是由 end 初始化的,所以指向最后一个元素的下一个位置。所以解引用时,rbegin 要到前一个位置,返回前一个位置的元素,rbegin不变,rbegin 和 rend 相等时循环结束。

反向迭代器的实现:

//反向迭代器可以封装一个正向迭代器,各种操作可以调用正向迭代器的函数
template<class Iterator, class Ref, class Ptr>
class ReverseIterator {
private:
	Iterator _it;
public:
	typedef ReverseIterator<Iterator, Ref, Ptr> Self;
	ReverseIterator()//调用正向迭代器的构造函数
    {}
	ReverseIterator(const Iterator& it)
	:_it(it)//调用正向迭代器的拷贝构造函数
	{}
	Ref operator*()//解引用指向前一个位置的元素,本身不变。
	{
		Iterator tmp = _it;
		return *(--tmp);
	}
	Self& operator++()//调用正向迭代器--函数
	{
		--_it;
		return *this;
	}
	Self& operator--()//调用正向迭代器++函数
	{
		++_it;
		return *this;
	}
	bool operator!=(const Self& s)//调用正向迭代器的!=函数
	{
		return _it != s._it;
	}
};

结语

优先级队列(priority_queue)实现较为简单,与数据结构中的堆很相似。实现优先级队列与实现栈和队列的区别是:栈和队列可以直接复用其他容器的函数,优先级队列则需要自己加一些东西进去加工,如向下调整,仿函数等。

仿函数替换成函数指针。函数指针很麻烦,写个函数声明很长,读懂也容易绕晕。仿函数就是一个类,类中可以写许多函数,封装的很好,使用时很方便。

反向迭代器(reverse_iterator)就是理解解引用(*)的过程,写起来就是封装一个正向迭代器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值