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

相关推荐
程序猿编码1 小时前
给你的网络流量穿件“隐形衣“:手把手教你用对称加密打造透明安全隧道
linux·开发语言·网络·安全·linux内核
aq55356001 小时前
编程语言三巨头:汇编、C++与PHP大比拼
java·开发语言
aq55356002 小时前
PHP vs Python:30秒看懂核心区别
开发语言·python·php
我是无敌小恐龙2 小时前
Java SE 零基础入门Day01 超详细笔记(开发前言+环境搭建+基础语法)
java·开发语言·人工智能·opencv·spring·机器学习
香蕉鼠片2 小时前
MFC是什么
c++·mfc
码云数智-大飞2 小时前
零基础微信小程序制作平台哪个好
开发语言
心态与习惯2 小时前
Julia 初探,及与 C++,Java,Python 的比较
java·c++·python·julia·比较
神仙别闹3 小时前
基于 MATLAB 实现的 DCT 域的信息隐藏
开发语言·matlab
techdashen3 小时前
Go 标准库 JSON 包迎来重大升级:encoding/json/v2 实验版来了
开发语言·golang·json
小欣加油3 小时前
leetcode2078 两栋颜色不同且距离最远的房子
数据结构·c++·算法·leetcode·职场和发展