您现在的位置是:首页 >技术杂谈 >【STL】stack、queue基本使用和模拟实现网站首页技术杂谈

【STL】stack、queue基本使用和模拟实现

LinAlpaca 2024-07-24 00:01:01
简介【STL】stack、queue基本使用和模拟实现

目录

前言

stack

接口介绍

模拟实现

queue

接口介绍

模拟实现

没有迭代器 

deque介绍


前言

stack 和 queue 本质上是一种容器配接器,就像我们平时充电时使用的电源适配器,能够将电压转换成设备能够接受的程度。

其通过封装特定容器作为其底层容器的类,通过一组特定的成员函数来实现结构的功能。


stack

?stack 就是 STL 中封装好的栈,在使用的时候我们不仅可以指定内部的数据类型,还可以指定内部的容器

?不指定容器其实也是可以的,内部的模板参数有一个缺省值

int main()
{
	stack<int, vector<int>> s1;     //内部容器为vector
	stack<int, list<int>> s2;       //内部容器为list
    stack<int> s3;                  //内部为默认容器deque
	return 0;
}

接口介绍

?库中还提供了一系列的接口,我们以前如何使用栈就如何使用 stack 即可,下面用一系列代码来示例一下。 

int main()
{
	stack<int> s;
	s.push(5);          //5
	s.push(6);          //5 6
	s.push(7);          //5 6 7
	s.push(8);          //5 6 7 8
	cout << s.size() << endl;   //打印栈的大小
	while (!s.empty())        //直到栈为空循环
	{
		cout << s.top() << endl;   //打印栈顶元素
		s.pop();                   //出栈
	}
	return 0;
}

 

模拟实现

?根据库中接口简单地实现一下 stack,需要注意的是这里复用的接口需要确保几个底部容器都要同时拥有

namespace Alpaca
{
	template<class T,class Container = deque<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()            //获取栈的大小
		{
			return _con.size();  //返回容器的大小
		}

		bool empty()              //栈的判空
		{
			return _con.empty();  //返回容器的判空
		} 

	private:
		Container _con;      //容器对象
	};
}

queue

?queue 则是封装出来的队列,与 stack 相反遵循着先进先出的原则(FIFO)。

?通过查询资料我们还发现,要作为 queue 的容器还需要支持以下操作:

?同样,使用 queue 时模板参数有两个,分别是内部元素的类型和内部容器,且内部容器缺省为 deque。

int main()
{
	queue<int> q1;                //传递缺省模板参数
	queue<int, list<int>> q2;     //指定内部容器为list
	return 0;
}

接口介绍

?队列与栈相反,其只在队尾入队,在队头出队。我们可以通过实例代码简单使用一下。

int main()
{
	queue<int> q;
	cout << "size is: " << q.size() << endl;   //查看队列大小
	q.push(1);                                 //数据入队
	q.push(2);
	q.push(3);
	q.push(4);
	q.push(5);
	cout << "size is: " << q.size() << endl;   
	cout << "first is: " << q.front() << endl; //查看首元素
	cout << "last is: " << q.back() << endl;   //查看最后一个元素
	while (!q.empty())                         //直到队空循环
	{
		cout << q.front() << " ";              //打印队头
		q.pop();                               //出队
	}
	cout << endl;
	cout << "size is: " << q.size() << endl;   //查看队列大小
	return 0;
}

模拟实现

?由于是泛型在使用的时候并不会对内部函数进行提示,简单理解就是我们复用对应容器的接口来实现队列的接口函数

namespace Alpaca
{
	template<class T,class Container = 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();
		}

		size_t size()                //获取队列大小
		{
			return _con.size();      //获取容器大小
		}

		bool empty()                 //队列判空
		{
			return _con.empty();     //容器判空
		}

	private:
		Container _con;
	};

}

没有迭代器 

?因为无论是 stack 和 queue 都是根据其特定的顺序进行元素的插入和删除,因此二者都不提供走访功能,也不提供迭代器。

deque介绍

?在出现 vector 和 list 之后,有人想到 vector 能够随机读取,但扩容和随机插入删除效率较低,而 list 插入删除效率都极高却无法随机访问,能不能实现一个结构能够同时兼顾 vector 和 list 的优点呢?

?于是 deque 就诞生了,deque 又叫双端队列,即一种双向开口的连续线性空间

?为了兼顾双端插入以及随机访问,deque 的底层是使用一个中控数组来管理一个个连续的空间,且第一个空间被开辟出来后是存放在中控数组的中央位置,之后不断插入数据,若一块连续空间已满只需要再开一块连续空间即可。也就是在中控数组中再增加一个指针。

?若是进行头插,则需要开辟一段新空间,将新的值存于连续空间的尾部

?得益于这种结构,即便是扩容所带来的拷贝消耗也是极低的,实际上不会像 vector 那样复杂的深拷贝,而只是对中控数组中的指针进行拷贝。同时  deque 也支持随机访问,只要直到每个连续空间的大小通过简单的计算便可以实现随机访问。

?若 deque 有这么多的有点,那为什么它还没有取代 vector 和 list 呢?这就不得不提到 deque 最为鸡肋的地方了,中部的插入删除操作相当复杂,若是直接在中部插入就要挪动当前空间的数据,更甚者还要牵扯到接下来的连续空间。不仅如此 deque 提供的优点不如 vector 和 list 那样极致,这也是为什么 deque 代替不了二者的原因。

?但若是有一种容器并不需要中间的插入删除,只需要在两端进行插入删除呢?于是 deque 就成为了适合 stack 和 queue 实现的内部容器


?好了,今天 stack、queue基本使用和模拟实现 的相关内容到这里就结束了,如果这篇文章对你有用的话还请留下你的三连加关注。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。