c++编程(19)——STL(5)容器适配器

欢迎来到博主的专栏——c++编程
博主ID:代码小豪


适配器adaptor

适配器看起来像一个容器,实际上adaptor并不属于容器的范畴,更像是一种专门用于某种容器的接口。

常用的适配器有stack、queue、priority_queue。

template <class T, class Container = deque<T> > class stack;
template <class T, class Container = deque<T> > class queue;
template <class T, class Container = vector<T>,
  class Compare = less<typename Container::value_type> > class priority_queue;

这些适配器的模板不仅仅能接收类型作为参数,也能接收容器的类型作为参数来实例化适配器。stack、queue、priority_queue允许用顺序容器来作为适配器的底层。顺序容器有vector、list、deque。因此这些适配器也被称为顺序容器适配器。

适配器的机制允许适配器选择一个容器作为底层,但是会提供特殊的接口来掩盖这些底层。比如以deque为底层的stack不允许头插元素,也不允许遍历整个容器。因此适配器更像是限定底层容器行为的容器

在下面,博主会模拟实现这三个适配器。但是相关的算法博主并不打算解释,因为这些内容博主已经在另一个专栏——c语言数据结构中讲过了。在文章的末尾,博主会附上链接。

stack

stack是一个先进后出(FILO)的数据结构,它只有一个头端的出口与入口,stack中插入元素、删除元素、访问元素都只能在头端进行操作。
在这里插入图片描述
由于stack的所有插入与删除的操作都在一端。因此,作为底层的容器必须支持在同一端进行数据的插入与删除操作,比如push_back,pop_back,push_front,pop_front等,因此可以作为stack适配器的底层容器有vectoor,deque,list。

而在这些容器当中,vector会有扩容造成额外的时间开销,而list的空间又较为臃肿,因此deque成为了stack最合适的底层容器,这是由于它拥有不错的头尾插入效率。

stack适配器的模拟实现如下:

template<class T,class container=deque<T>>
class stack
{
public:
	stack(const container& copy_con=container())
		:_con(copy_con) {;}

	void push(const T& val){_con.push_back(val);}
	void pop() { _con.pop_back(); }
	T& top() { return _con.back(); }
	const T& top()const { return _con.back(); }
	bool empty()const { return _con.empty(); }
	size_t size()const { return _con.size(); }
private:
	container _con;//底层容器
};

其实从这段代码就很好的体现了,适配器更像是限定底层容器行为的容器这一特性。因此从代码中可以发现,stack并没有设计属于自己的操作,也没有除底层容器以外的数据成员,stack适配器中一切对于stack操作的函数,其实是在对底层容器进行操作。比如像stack上push一个元素,实际上是在容器的尾端插入元素,取stack栈顶元素,实际上是在取容器的尾端元素。

从这里就可以看出,适配器其实是一个封装了底层容器的接口。拿一个例子举例,底层容器就像一个电灯泡,而适配器就是这个电灯泡的灯罩,如果我们用不同的灯罩来封装灯泡,其发光的效果会发生变化,但是底层的电灯泡是不会变的,只是灯罩变了,于是效果也就跟着变了。

从下面这段测试案例,也能佐证这个性质:

我们可以用性质不同的容器来作为stack的底层容器,但是由于stack限制了这些容器的操作,因此它们看起来没有两样

void testmystack()
{
	stack<int, vector<int>> stack1;//vector为底层
	stack1.push(1);
	stack1.push(2);
	stack1.push(3);
	stack1.push(4);
	stack1.push(5);
	while (!stack1.empty())
	{
		cout << stack1.top();
		stack1.pop();
	}
	cout << endl;

	stack<int, list<int>> stack2;//list为底层
	stack2.push(1);
	stack2.push(2);
	stack2.push(3);
	stack2.push(4);
	stack2.push(5);
	while (!stack2.empty())
	{
		cout << stack2.top();
		stack2.pop();
	}
	cout << endl;
}

后续不再放出测试案例

queue

queue是一种先进先出(FIFO)的数据结构,它有两个端,头端作为出口,尾端作为入口,queue只允许在尾端插入元素,在头端删除元素,以及只允许在头端访问元素
在这里插入图片描述

queue只允许在尾端插入数据,在头端删除数据,因此适配的底层容器必须拥有头尾删除的操作,比如list和deque,它们都有在双端删除、插入的函数。而vector则不行,因为vector没有push_front和pop_front。即没有双端插入、删除元素的能力。

template<class T, class container = deque<T>>
class queue
{
public:
	queue(const container& copy_con = container())
		:_con(copy_con) {;}

	void push(const T& val) { _con.push_back(val); }
	void pop() { _con.pop_front(); }
	const T& front()const { return _con.front(); }
	T& front() { return _con.front(); }
	bool empty() { return _con.empty(); }
	size_t size() { return _con.size(); }

private:
	container _con;
};

priority_queue

priority_queue是一个很特殊的适配器,和stack与queue不同,priority拥有属于自己的算法,即堆算法,priority的底层容器虽然用的是vector和deque,但是实际上用的其实是heap,即堆。heap并不是STL中的容器,或者说它是一个只藏在算法中的容器。在一些STL的一些算法中可以看到heap的影子。

博主在这里并不像介绍堆算法,因此很早之间博主就已经实现过了,在这篇博客当中,博主更希望读者能够对适配器有一定的了解。

因此我们直接放上源代码吧。

template<class T,class container=deque<T>,class comp=less<T>>
class priority_queue

priority_queue存在一个目前尚未了解的STL组件,仿函数,博主将会不就得将来给大家带来仿函数的讲解,我们先在priority_queue当中一睹芳容。

这里仿函数传递的是STL中定义的仿函数less,其作用是生成一个大堆,如果想要priority_queue的底层算法用的是小堆,可以传入仿函数greater。

priority_queue的译名是优先级队列,在priority_queue当中插入的元素会按照某种规则排序(即大小堆的排序规则)。当我们pop掉一个元素时,priority_queue会将内部元素重新排列,以保持pop的元素总是队列中的最大值或最小值。(关于heap的数据结构,博主会在文末放上链接)

template<class T,class container=deque<T>,class comp=less<T>>
class priority_queue
{
public:
	priority_queue()
		:_con(container()) {;}

	template<class inputiterator>
	priority_queue(inputiterator first, inputiterator last)
	{
		while (first != last)
		{
			_con.push_back(*first);
			first++;
		}
		create_heap();
	}

	void push(const T val)
	{
		_con.push_back(val);
		adjustup(_con.size()-1);
	}

	void pop()
	{
		swap(_con[0], _con.back());
		_con.pop_back();
		adjustdown(0);
	}

	const T& top()const { return _con.front(); }
	T& top() { return _con.front(); }
	size_t size(){ return _con.size(); }
	bool empty() { return _con.empty(); }	
private:
	void create_heap()//向下调整建堆算法
	{
		for (int i = (_con.size() - 2) / 2; i >= 0; i--)
		{
			adjustdown(i);
		}
	}
	void adjustdown(int parent)//向下调整算法
	{
		int child=parent*2+1;
		while (child < _con.size())
		{
			if (child + 1 < _con.size() && compare(_con[child], _con[child + 1]))
			{
				child++;
			}
			if (compare(_con[parent], _con[child]))
			{
				std::swap(_con[parent],_con[child]);	
			}
			else
				break;
			parent=child;
			child = parent * 2 + 1;
		}
	}
	void adjustup(int child)//向上调整算法
	{
		int parent=(child-1)/2;
		while (parent != child)
		{
			if (compare(_con[parent] , _con[child]))
			{
				std::swap(_con[parent], _con[child]);
			}
			else
				break;
			child = parent;
			parent = (child - 1) / 2;
		}
	}

	container _con;
	comp compare;
	};
}

传送门:

数据结构——栈
数据结构——队列
数据结构——堆

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

代码小豪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值