STL中queue、stack的实现与容器适配器的讲解

目录

简介

栈(Stack)

队列(Queue)

实现

栈的实现

队列的实现

deque的讲解

deque的结构示意图


简介

栈(Stack)和队列(Queue)是两种基本的数据结构,在STL(Standard Template Library,标准模板库)中,它们被封装为容器适配器,用于管理元素的插入和删除操作。

栈(Stack)

栈是一种后进先出(Last In First Out, LIFO)的数据结构。在STL中,栈的接口定义如下:

  • push():在栈顶插入一个元素。
  • pop():删除栈顶元素。
  • top():返回栈顶元素的引用。
  • empty():检查栈是否为空。
  • size():返回栈中元素的数量。

栈通常用于以下场景:

  • 递归算法的实现。
  • 实现后退功能,如浏览器的后退按钮。
  • 解决问题时的临时存储,如深度优先搜索(DFS)。

队列(Queue)

队列是一种先进先出(First In First Out, FIFO)的数据结构。在STL中,队列的接口定义如下:

  • push():在队列末尾插入一个元素。
  • pop():删除队列首部的元素。
  • front():返回队列首部元素的引用。
  • back():返回队列末尾元素的引用。
  • empty():检查队列是否为空。
  • size():返回队列中元素的数量。

队列通常用于以下场景:

  • 在多任务处理中管理任务,如线程池。
  • 实现广度优先搜索(BFS)。
  • 缓存实现,如最近最少使用(LRU)缓存。

在STL中,栈和队列通常是基于deque(双端队列)或list(链表)来实现的,但用户不需要关心底层实现细节,可以直接使用它们提供的接口来进行操作。这两种数据结构都是模板类,可以存储任何类型的元素。

由于我们需要深度学习这两种数据结构,因此本文将实现stack与queue

实现

栈的实现

栈可分数组站、链表栈,都可以实现后进先出的功能。我们可以采用C语言中借助一个动态指针等方式实现。但是在C++中,可以借助其他容器实现。

对于stack而言,既可以使用list,也可以使用vector、、、

cpp 复制代码
template<class T, class Container = std::deque<T>>
class stack
{

官网中stack的实现借助的是模板

因此我们也借助模板实现。

模板参数由数据类型T和容器类型Container构成。

栈的功能函数

栈主要是top pop push这三个核心函数构成。

cpp 复制代码
namespace My_Stack
{
	template<class T, class Container = std::deque<T>>
	class stack
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}

		void pop() 
		{
			_con.pop_back();
		}

		const T& top() const
		{
			return _con.back();		//back不是front
		}

		bool empty() const
		{
			return _con.empty();
		}

		size_t size() const
		{
			return _con.size();
		}
	private:
		Container _con;		//默认生成的构造函数,会调用_con的构造
	};

}

需要注意的是:

对于构造函数、析构函数等内置函数,我们并没有显式实现。系统默认生成的构造函数会自动调用_con该容器的构造,析构函数也会调用该容器的析构完成资源的清理。

top是栈顶元素,也就是容器的最后一个元素。

队列的实现

队列也是一个模板类,但是队列的容器不能是vector,原因是vector没有pop_front()函数,而队列需要大量的先进先出。

cpp 复制代码
#include <deque>

namespace My_Queue
{
	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& back()
		{
			return _con.back();
		}

		const T& front() const
		{
			return _con.front();
		}

		bool empty()
		{
			return _con.empty();
		}

		size_t size()
		{
			return _con.size();
		}

	private:
		Container _con;
	};


}

deque的讲解

可以看到,我们在实现队列和栈的时候,都利用了deque,这是什么呢?

Deque(双端队列)是一种数据结构,它允许在队列的两端进行插入和删除操作 。它是"double-ended queue"的缩写,有时也被称为双端队列容器

在 C++ 的 STL(标准模板库)中,deque 与 vector 容器有很多相似之处,但它们之间也存在一些关键的区别。与 vector 不同的是,deque 在队列的两端(头部和尾部)添加或删除元素的效率都非常高,时间复杂度为 O(1)。这意味着deque 在频繁进行头部或尾部操作时,比 vector 更高效

其实deque同样可以成为完成栈和队列的容器。

以下是 deque 的一些特点:

  1. 双向开口:deque 是一种双向开口的连续线性空间,允许在头尾两端分别进行元素的插入和删除操作。

  2. 动态空间管理:deque 没有固定的容量概念,它是动态地以分段连续空间组合而成,可以根据需要随时增加新的空间。

  3. 高效的头尾操作:与 vector 相比,deque 在头部进行插入和删除操作时效率更高,因为不需要移动所有元素。

  4. 迭代器:deque 的迭代器需要能够指出分段连续空间的位置,并且能够判断是否处于当前缓冲区的边缘。如果是,则在进行前进或后退操作时,需要跳跃到下一个或上一个缓冲区。

如下是deque的函数

看函数,像是list与vector的结合体。它既能实现list的功能,也能实现vector的功能。那为什么不能deque呢?主要还是,他什么都会,但是什么都不精通

因此,根据需求,我们还是主要使用list和vector。但是,当需要在序列的两端频繁进行添加或删除操作时,deque 容器是一个很好的选择。

deque的结构示意图

deque其实是一个中控数组实现,数组中存放着一个个的指针,指向了其他容器(一般被乘坐buffer)。

其实,本质是个指针数组!

头插与尾插示意图

头插:在10之前

中控满了,可以去扩容,但是代价低,因为这只是一个指针数组,只需要拷贝指针。

但是在内部插入删除元素,就显得效率极其低下!因为要么得整体挪动数据,以保证buffer的大小一致,方便[]访问;要么得对单个的buffer扩容,或者减少数据。

相关推荐
I_Am_Me_10 分钟前
【JavaEE进阶】 JavaScript
开发语言·javascript·ecmascript
暮色_年华12 分钟前
Modern Effective C++item 9:优先考虑别名声明而非typedef
c++
重生之我是数学王子20 分钟前
QT基础 编码问题 定时器 事件 绘图事件 keyPressEvent QT5.12.3环境 C++实现
开发语言·c++·qt
Ai 编码助手22 分钟前
使用php和Xunsearch提升音乐网站的歌曲搜索效果
开发语言·php
学习前端的小z26 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
神仙别闹33 分钟前
基于C#和Sql Server 2008实现的(WinForm)订单生成系统
开发语言·c#
XINGTECODE34 分钟前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang
我们的五年44 分钟前
【Linux课程学习】:进程程序替换,execl,execv,execlp,execvp,execve,execle,execvpe函数
linux·c++·学习
zwjapple1 小时前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five1 小时前
TypeScript项目中Axios的封装
开发语言·前端·javascript