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扩容,或者减少数据。

相关推荐
码农小河663 小时前
基于PHP的电商后台蜜罐系统
开发语言·php
starzy19903 小时前
深入理解Scala柯里化函数:函数链式变换
开发语言·scala
ShineWinsu3 小时前
对于Qt:QTreeWidget常见属性以及QTreeWidgetItem类的解析
开发语言·c++·qt·gui·客户端·多叉树·qtreewidget
落羽的落羽4 小时前
【C++】C++17的主要特性
linux·服务器·开发语言·c++·算法·机器学习
总要学点什么4 小时前
第108篇:三维特效-图片轨迹线效果——把贴图滚成一串箭头,想用它的本色反而整条消失
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
starzy19909 小时前
深入理解Scala隐式值:编译期自动装配
开发语言·后端·scala
布莱克6059 小时前
Swift 条件语句详解:从 if 到 switch 的完整指南
开发语言·ssh·swift·switch·条件语句·guard·if
汉克老师9 小时前
《从C++小白到算法高手:GESP 1—8级成长地图》第一讲:认识GESP——从编程新手到算法探索者
c++·gesp·信息学竞赛·小学生学c++
吴可可1239 小时前
ActiveX调用WScript.Shell原理揭秘
开发语言
晴天的雨.99210 小时前
【C++】继承篇
java·开发语言·c++