栈和队列&优先级队列&适配器原理

栈和队列接口函数

stack 栈接口函数

因为栈的结构限制,栈只能栈顶push和栈顶pop,

所以接口略少

queue 队列接口函数

队列只比栈多了一个接口:back
队列的front相当于栈的top

适配器

栈的类模板

其中第二个参数是Container,

且缺省参数为deque<T>

到这就可以引入一个概念:适配器

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。

可以类比为

在数据结构的栈中,这种适配器是什么?

在C语言阶段实现栈时,我们
使用的底层是顺序表来实现,也就是
把顺序表做了一层封装和限制,让它
的功能变得和栈一样,C++这里也是一样!

实现栈时不用再去写一个顺序表
而是直接调用库中的vector!

队列的类模板

其中第二个参数是Container,

且缺省参数为deque<T>

和stack是一样的

栈和队列模拟实现

namespace wr
{
	template<class T, class Container>
		class stack
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_back();
		}
		const T& top()
		{
			return _con.back();
		}
		bool empty()
		{
			return _con.empty();
		}
		size_t size()
		{
			return _con.size();
		}
	private:
		Container _con;
	};
}

队列

namespace wr1 
{
	template<class T,class Container = std::deque<T>>
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_front();
		}
		const T& front()
		{
			return _con.front();
		}
		const T& back()
		{
			return _con.back();
		}
		bool empty()
		{
			return _con.empty();
		}
		size_t size()
		{
			return _con.size();
		}
	private:
		Container _con;
	};

}

我们和库中的缺省值保持一致,使用deque

这样实现栈十分方便,因为此时的栈就好像继承家业一样,

不用再去写构造和析构函数,默认生成的构造析构函数

会去调用内嵌类型的构造或析构,

在基础上实现接口函数就可以了。

deque基本介绍

deque也是STL库中的一个容器:

deque又被称为双端队列是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素,与list比较,空间利用率比较高(啥都能干,啥都干的不是特别好,既支持头插头删也有尾插尾删,也支持[]

接口函数

deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:

deque通过一个叫中控数组的来控制(map箭头指向的数组)

deque扩容是直接另外开辟一份空间
再让中控数组指向新开辟的空间
再将原先空间的内容拷贝至新空间

优先级队列

优先级队列:priority_queue

它也是一个容量适配器

优先级队列默认是大堆

它的底层适配器默认是vector

优先级队列默认有三个类模板,第三个
模板就是决定此优先级队列是大堆还是小堆
它是仿函数,默认的less是大堆
我们显示传参greater时是小堆!

优先级队列的接口函数:

如果你想使用小堆,就要将前两个
模板参数实例化后才能实例化第三个
当less变成greater时,就是小堆

如果要改变大小堆,需要将参数补全,vector<int>也需要写上

优先级队列的模拟实现

由于优先级队列实际上就是个堆
所以在插入,删除之后.都需要做一件事
那就是向上调整或向下调整!所以插入和
删除的关键其实就在于向上/下调整!

向上调整:

		void up(size_t child)
		{
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
				if (_con[child] > _con[parent])
				{
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (parent - 1) / 2;
				}
				else
					break;
			}
		}

向下调整:

		void down(size_t parent)
		{
			size_t child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && _con[child + 1] > _con[child])
				{
					++child;

				}
				if (_con[child] > _con[parent])
				{
					std::swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
					break;
			}
		}

全部代码:

namespace wr2 
{
	template<class T,class Container=std::vector<T>>
	class priority 
	{
	public:		
		size_t size()
		{
			return _con.size();
		}
		void up(size_t child)
		{
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
				if (_con[child] > _con[parent])
				{
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (parent - 1) / 2;
				}
				else
					break;
			}
		}
		void down(size_t parent)
		{
			size_t child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && _con[child + 1] > _con[child])
				{
					++child;

				}
				if (_con[child] > _con[parent])
				{
					std::swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
					break;
			}
		}

		void push(const T& x)
		{
			_con.push_back(x);
			up(_con.size() - 1);
		}
		void pop()
		{
			std::swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			down(0);
		}
		const T& top()
		{
			return _con[0];
		}
		bool empty()
		{
			return _con.empty();
		}

	private:
		Container _con;
	};

}

总结

通过前面的容器学习,栈和队列学习起来也更加得心应手,

不仅仅是模拟实现,可以复用以前的容器,

连使用方法和函数接口都和之前一样,

而deque更像一个”六边形战士“,

不过各项能力都不出众。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值