容器适配器

在C++中,对于容器适配器的理解我们可以举个例子,比如现在给定一个容器vector,而我们希望它以栈的样子呈现出来,也就是可以支持后进先出,有栈的一些基本接口的操作等,此时我们不需要自己再重新写一个数据结构,而是把原来的容器vector封装一下,改变它的接口,就可以正常使用了,下面我用一段代码来实现栈去适配vector的操作:

#pragma once
#include <iostream>
#include <vector>
using namespace std;
template <class T, class Container = vector<T>>
class Stack
{
public:
	void Push(const T& x)
	{
		_con.push_back(x);
	}
	void Pop()
	{
		_con.pop_back();
	}
    const T& Top()
	{
		return _con.back();
	}
	size_t Size() const
	{
		return _con.size();
	}
	bool Empty()
	{
		return _con.empty();
	}
private:
	Container _con;
};
//测试函数
void TestStack()
{
	Stack<int, vector<int>> st;
	//模板参数给了缺省值,template <class T, class Container = vector<T>>
	Stack<int> st;
	st.Push(1);
	st.Push(2);
	st.Push(3);
	st.Push(4);
	st.Push(5);
	while (!st.Empty())
	{
		cout << st.Top()<<" ";
		st.Pop();
	}
	cout << endl;
}

由以上代码可以看出,实现上是用栈来实现的,但是底层封装的容器是vector;当然也可以选用list作为它的底层容器,其原理都是一样的。
下面我再附上队列和优先级队列的关于适配器的实现代码:
//队列

#pragma once
#include <iostream>
#include <queue>
#include <list>
using namespace std;
template<class T,class Container>
class Queue
{
public:
	void Push(const T& x)
	{
		_con.push_back(x);
	}
	void Pop()
	{
		_con.pop_front();
	}
	const T& back()
	{
		return _con.back();
	}
	const T& Front()
	{
		return _con.front();
	}
	bool Empty()
	{
		return _con.empty();
	}
	size_t Size()
	{
		return _con.size();
	}
private:
	Container _con;
};

void TestQueue()
{
	Queue<int,list<int>> q;
	q.Push(1);
	q.Push(2);
	q.Push(3);
	q.Push(4);
	q.Push(5);
	while (!q.Empty())
	{
		cout << q.Front() << " ";
		q.Pop();
	}
	cout << endl;

}

//优先级队列
此时需要注意的是,优先级队列的底层结构是堆,再插入和删除的时候需要调整。

#pragma once
#include <iostream>
using namespace std;
#include <vector>
#include <queue>
#include <functional>
template<class T,class Container=vector<T>,class Compare=less<T>>
class PriorityQueue
{
public:
	//大堆
	void Adjustup(int child)
	{
		int parents = (child - 1) / 2;
		while (child>0)
		{
			Compare com;
			//重载operator()
			if (com(_con[parents] , _con[child]))
			{
				swap(_con[parents], _con[child]);
				child = parents;
				parents = (child - 1) / 2;
			}
			else
				break;
		}
	}
	void Adjustdown(size_t parents)
	{
		int child = parents * 2 + 1;
		while (child < _con.size())
		{
			Compare com;
			//找出左右孩子大的
			if (child + 1 < _con.size() && com(_con[child] ,_con[child + 1]))
				child++;
			if (com(_con[parents] , _con[child]))
			{
				swap(_con[parents], _con[child]);
				parents = child;
				child = parents * 2 + 1;
			}
			else
				break;
		}
	}
	void Push(const T& x)
	{
		_con.push_back(x);
		Adjustup(_con.size()-1);
	}
	void Pop()
	{
		swap(_con[0], _con[_con.size() - 1]);
		_con.pop_back();
		Adjustdown(0);
	}
	const T& Top()
	{
		return _con[0];
	}
	const T& Back()
	{
		return _con[_con.size() - 1];
	}
	bool Empty()
	{
		return _con.empty();
	}
	size_t Size()
	{
		return _con.size();
	}
private:
	Container _con;
};
//测试函数
void TestPriorityQueue()
{
	PriorityQueue<int, vector<int>,greater<int>> q;
	q.Push(1);
	q.Push(2);
	q.Push(3);
	q.Push(4);
	q.Push(5);

	while (!q.Empty())
	{
		cout << q.Top() << " ";
		q.Pop();
	}
	cout << endl;
}

然而在C++中,选择deque作为stack和queue的底层默认容器,原因主要有以下几点:
1、stack和queue不需要遍历(因此栈和队列都没有迭代器),只需要在固定的一端或者两端进行操作;
2、在stack元素增长时,deque比vector效率更高;queue中的元素增长时,deque不仅效率高,而且内存使用率高。
但是vector和list作为栈和队列的底层容器,有一些优缺点,例如vector可以支持随机访问,连续内存数组缓存利用率高;但是头部插入删除效率低,扩容成本高;list的优缺点和vector反之,这里我就不再一一陈述。
所以对于以上list和vector的优缺点问题,利用deque(双端队列)可以折中的解决。由于deque在现实问题中并不常用,我就对于它的实现不做介绍了,有需要的可以去看源代码。

上一篇:parent(),parents()与closest()的区别与详解


下一篇:移动开发者如何赚钱