【C++】stack和queue 适配器

news2024/11/30 0:36:53

                                                

🔥个人主页:北辰水墨

🔥专栏:C++学习仓

Alt

本节内容我们来讲解栈和队列的模拟实现,文末会赋上模拟实现的代码

 一、stack的使用和模拟实现

 stack适配器的介绍:

1.  stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。

2.  stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。

3.  stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作

  • empty:判空操作
  • back:获取尾部元素操作
  • push_back:尾部插入元素操作
  • pop_back:尾部删除元素操作

4.  标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器,默认情况下使用deque。 

函数的介绍:

🔥初始化栈:

1.构建一个空栈:

std::stack<int> myst;

2.通过已有的容器,初始化栈:

std::vecotr<int> v={1,2,3,4,5};

srd::stack<int,vector<int>> myst(v); 

//stack是适配器,传相应的容器,底层就是用对应的容器实现栈

3.使用拷贝构造函数初始化一个与另一个栈相同的栈:

std::stack<int> originalStack;
// 添加一些元素到 originalStack
std::stack<int> myStack(originalStack);

 

🔥empty() 

检查栈是否为空

🔥size()

返回stack中元素的个数

🔥top()

返回栈顶元素

🔥push() 

 将元素val压栈

🔥pop()

将stack中尾部的元素弹出 

🔥swap()

交换两个栈的所有元素

// stack::swap
#include <iostream>       // std::cout
#include <stack>          // std::stack

int main ()
{
  std::stack<int> foo,bar;
  foo.push (10); foo.push(20); foo.push(30);
  bar.push (111); bar.push(222);

  foo.swap(bar);

  std::cout << "size of foo: " << foo.size() << '\n';
  std::cout << "size of bar: " << bar.size() << '\n';

  return 0;
}

输出:

size of foo: 2

size of bar: 3

 

例题一:"最小栈"

(点击"最小栈"字体就可以跳转做题)

  1. st1 是一个标准的栈,它用于按照后进先出的顺序存储所有推入的元素
  2. st2 是一个辅助栈,它用于跟踪 s1 中所有元素的最小值
  •  MinStack() {} 不需要自己实现,走初始化列表,是自定义类型,调用他自己stack<int>的默认构造

  • void push(int x):在 st1 中推入 x。如果 st2 为空或者 x 小于等于 st2 的栈顶元素,也将 x 推入 st2这保证 st2 的栈顶元素始终是 st1 中当前所有元素的最小值

  • void pop()从 st1 中弹出一个元素。如果 st1 的栈顶元素与 st2 的栈顶元素相等,说明 st1 弹出的元素是当前的最小值,因此也需要在 st2 中弹出栈顶元素

class MinStack {
public:
    /** initialize your data structure here. */
    MinStack() {
    }
    
    void push(int x) {
        st1.push(x);
        if(st2.empty()||st2.top()>=x) st2.push(x);
    }
    
    void pop() {
        if(st1.top()==st2.top())
        {
            st2.pop();
        }
        st1.pop();
    }
    
    int top() {
        return st1.top();
    }
    
    int getMin() {
        return st2.top();
    }
private:
    stack<int> st1;
    stack<int> st2;
};

例题二:"验证栈序列"

 核心思想:模拟入栈和出栈的过程。

n: 记录poped的下标

pushst:创建一个栈

 for循环开始模拟入栈的过程,只要 i 没有大于 pushed.size() 个数就继续循环

while循环:栈不为空&&栈顶元素==poped[n]就进入循环,出栈并且让n++

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> pushst;
    int n = 0;
    for(int i=0;i<pushed.size();i++)
    {
	    pushst.push(pushed[i]);
	    while (!pushst.empty()&&pushst.top() == popped[n]) //这个的顺序不能反,否则当为空的时候,去取栈顶元素,会报错
	    {
	    	pushst.pop();
	    	n++;
	    }
    }
    return pushst.empty();
    }
};

 

栈的模拟实现:

namespace ink
{
	template<class T,class Container=vector<int>>
	class stack
	{
	public:
		bool empty()
		{
			return _con.empty();
		}
		size_t size()
		{
			return _con.size();
		}
		//加引用,是为了自定义类型,减少拷贝构造
		const T& top()  //const 返回的是栈顶元素,用引用返回,外面的就可以接收并且可以修改,我就让它只读
		{
			return _con.back();
		}
		void pop()
		{
			_con.pop_back();
		}
		void push(T& x)
		{
			_con.push_back(x);
		}
	private:
		Container _con;
	};
}

上面的实现是简单地展示了如何用C++模板和通用编程的原则来定义一个通用的栈类,这个栈类被称为适配器。在这种上下文中,“适配器模式”是一种设计模式的用词。

在容器类库设计中(如标准模板库 STL 中的容器),适配器模式通常用于通过已有的容器类型(如vectordequelist等),来实现某种特定的抽象数据类型(如栈、队列等)的接口。这样的做法使我们能够重用现有代码,并提供更丰富的操作 

  • 定义了 stack 模板类,它接收两个模板参数:
    • T: 栈中元素的类型。
    • Container: 底层容器的类型,默认是 vector<T>

Container 是一个模板参数,它允许我们定义底层数据结构。默认使用 std::vector<T> 作为底层容器,但我们可以指定 std::deque<T>std::list<T>等容器,这是适配器模式的应用之一,我们可以切换不同的底层实现,不改变栈的接口 (底层千差万别,不改变上层接口)

stack 类包含如下成员函数:

  • push: 向栈中添加元素
  • pop: 从栈中移除顶部元素
  • size: 返回栈中元素的数量
  • empty: 检查栈是否为空
  • top: 返回栈顶元素的引用

这些成员函数中的每一个都直接调用了底层容器 Container 实例 _con 的相应操作函数,这样 stack 就提供了类似栈的接口

 

二、queue的使用和模拟实现

queue适配器的介绍

1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素

2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。

3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:

  • empty:检测队列是否为空
  • size:返回队列中有效元素的个数
  • front:返回队头元素的引用
  • back:返回队尾元素的引用
  • push_back:在队列尾部入队列
  • pop_front:在队列头部出队列

 4.队列需要支持pop_front的操作,而vector不支持。所以vector不能做queue的底层容器

5.一般我们都是使用deque容器作为queue的底层容器。

deque容器的介绍

 

deque 成为双端队列,是一种序列容器,在两端都支持高效的元素插入和删除操作。

与 std::vector 相比,std::deque 提供类似的功能,但在许多实现中,deque 是由多个固定大小的数组(通常被称为块或段)组成的动态数组。这允许在两端进行快速的插入和删除操作,而不必像 std::vector 在插入(或删除)元素时将所有元素向前或向后移动。

deque 的主要特点和功能包括:

1. 双端操作:可以在队列的前端和后端进行插入 (push_front, emplace_front) 和删除 (pop_front) 操作


2. 序列访问:可以使用下标操作符 (operator[]) 或一系列迭代器访问 deque 中的元素

3. 迭代器失效:在两端添加或删除元素通常不会使迭代器失效,但是在 deque 中除了首尾外的任何位置插入或删除元素都可能使所有迭代器失效。这取决于具体的实现。

4. 内存分配:deque 不保证所有元素都连续存储,因此不能依赖像 std::vector 那样的内存连续性

5. 性能:在两端插入或删除元素通常是常数时间复杂度 O(1),但是在中间位置插入或删除元素的时间复杂度通常是线性的 O(n),这取决于插入位置与最近端点的距离

vector的优点在于能支持下标随机访问,缺点是头部或中间插入删除的效率低,扩容有消耗

list的优点在于任意位置插入删除的效率都不错,缺点就是不支持下标的随机访问

而deque可以看做vector和list的中和版,既支持下标访问,又支持头插头删

deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的

std::deque 的常见实现方式是使用一系列的固定大小的数组(称为缓冲区或块),这些数组被指针所管理,这些指针通常保存在一个或多个中央数组中。这种实现允许在 deque 的两端都高效地添加或删除元素,而无需移动所有元素 

双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂 

中控数组满了就扩容,它的消耗会小很多

 

它的迭代器有四个指针

  • start指向指向第一个buff的第一个数据
  • finish指向最后一个buff的最后一个数据的下一个位置
  • cur指向buff的头节点
  • node指回中控数组(为了让迭代器在走完上一层之后可以跳转到下一块空间的首元素)

deque的缺陷

与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。

 与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段

 但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构

为什么选择deque作为stack和queue的底层默认容器?

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:

  1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。
  2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);deque不需要像list那样频繁的开空间,queue/stack中的元素增长时,deque不仅效率高,而且内存使用率高

 

queue的模拟实现

#include<deque>
#include<list>
namespace own
{
    template<class T, class Con = deque<T>>
    class queue
    {
    public:
        queue() {}
        void push(const T& x) { _c.push_back(x); }
        void pop() { _c.pop_front(); }
        T& back() { return _c.back(); }
        const T& back()const { return _c.back(); }
        T& front() { return _c.front(); }
        const T& front()const { return _c.front(); }
        size_t size()const { return _c.size(); }
        bool empty()const { return _c.empty(); }
    private:
        Con _c;
    };
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1667934.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis的数据淘汰策略——Java全栈知识(19)

Redis的数据淘汰策略 什么是数据淘汰策略 数据过期策略是 redis 中设置了 TTL 的数据过期的时候 Redis 的处理策略。数据淘汰策略是 Redis 内存不够的时候&#xff0c; 数据的淘汰策略&#xff1a;当 Redis 中的内存不够用时&#xff0c;此时在向 Redis 中添加新的 key, 那么…

物联网设计竞赛_2_Ubuntu联网配置

采用nat配置 随便定义一个VMnet虚拟网络接口&#xff0c;定义成nat模式 如果主机用的校园网&#xff0c;那么虚拟机发送消息将通过nat转换&#xff0c;转换成用户校园网ip进行发送&#xff0c;发送到校园网路由器再经过nat转换成公网ip访问互联网 点击NAT设置和DHCP设置记录好…

3kCTF2021 echo klibrary

文章目录 前言echoklibrary 前言 今天状态不好&#xff0c;很多事情都不想干&#xff0c;就做一做简单的题目 echo 内核版本&#xff1a;v5.9.10smap/smep/kaslr 开启modprobe_path 可写 题目给了源码&#xff0c;非常简单就是无限次的任意地址读写&#xff1a; #include …

js逆向-某投资平台参数分析

声明 本文仅供学习参考&#xff0c;如有侵权可私信本人删除&#xff0c;请勿用于其他途径&#xff0c;违者后果自负&#xff01; 如果觉得文章对你有所帮助&#xff0c;可以给博主点击关注和收藏哦&#xff01; 分析 aHR0cDovLzIyMS4yMTQuOTQuNTE6ODA4MS9pY2l0eS9pcHJvL2hhb…

如何在适用于 Linux 的 Visual Studio Code 中使用 .NET 8 上的 FastReport Avalonia

我们将继续撰写有关在各种操作系统上的 Visual Studio Code 中使用 FastReport Avalonia 的系列文章。在本文中&#xff0c;我们将详细分析如何使用 Visual Studio Code IDE 在 Linux 操作系统上运行 FastReport Avalonia。 Avalonia UI 是一个积极用于开发跨平台用户界面的 .…

Keysight 是德 N1077B 光/电时钟恢复设备,收藏保存

Keysight N1077B是一款光/电时钟恢复设备&#xff0c;支持115 MBd至24 GBd的数据速率范围&#xff0c;适用于多模和单模光信号以及电信号。该设备能够处理PAM4和NRZ两种类型的数据信号&#xff0c;并提供符合标准的时钟恢复功能。 N1077B具备可调峰值和环路带宽&#xff08;高…

第一课,idle的使用

一&#xff0c;什么是python&#xff1f; 是咱们用来和计算机“交流”、“发号施令”的编程语言。但是&#xff0c;计算机是看不懂python的&#xff0c;我们还需要一个翻译官&#xff0c;把python翻译成0和1组成的二进制&#xff0c;才能让计算机明白&#xff01; 0000001111…

四、VGA项目:联合精简帧+双fifo+sobel算法 实现VGA显示

前言&#xff1a;该项目实际上是在很多基础的小练习上合成起来的&#xff0c;例如涉及到uart&#xff08;rs232&#xff09;的数据传输、双fifo流水线操作、VGA图像显示&#xff0c;本次内容在此基础上又增添了sobel算法&#xff0c;能实现图像的边沿监测并VGA显示。 文章目录…

部署tomcat部署LNAMT

这里写目录标题 部署tomcatjava环境安装 部署LNAMT更改tomcat端口号 tomcat就是中间件之一&#xff0c;tomcat本身是一个容器&#xff0c;专门用来运行java程序&#xff0c;java语言开发的网页.jsp就应该运行于tomcat中。而tomcat本身的运行也依赖于jdk环境。 部署tomcat java…

Mask2former代码详解

1.整体流程 Mask2former流程如图所示&#xff0c;对于输入图片&#xff0c;首先经过Resnet等骨干网络获得多层级特征&#xff0c;对于获得的多层级特征&#xff0c;一个方向经过pixel decoder(基于DetrTransformerEncoderLayer)得到per-pixel embedding,另外一个方向经过transf…

【实战】算法思路总结

面试过程中&#xff0c;总是被拷打&#xff0c;信心都要没了。但是也慢慢摸索出一些思路&#xff0c;希望对大家有帮助。 &#xff08;需要多用一下ACM模式&#xff0c;力扣模式提供好了模板&#xff0c;自己在IDEA里面写的话&#xff0c;还是会有些陌生&#xff09; 0、基本…

Kafka的安装及接入SpringBoot

环境&#xff1a;windows、jdk1.8、springboot2 Apache KafkaApache Kafka: A Distributed Streaming Platform.https://kafka.apache.org/ 1.概述 Kafka 是一种高性能、分布式的消息队列系统&#xff0c;最初由 LinkedIn 公司开发&#xff0c;并于2011年成为 Apache 顶级项目…

【C/C++】内存分布

本文第一部分主要介绍了程序内存区域的划分以及数据的存储。第二部分有一段代码和一些题目&#xff0c;全面直观得分析了程序中的数组在内存中的存储。 因为不同的数据有不同的存储需求&#xff0c;各区域满足不同的需求&#xff0c;所以程序内存会有区域的划分。 根据需求的不…

第02章 计算机网络概述

2.1 本章目标 了解计算机网络的定义了解计算机网络的功能了解计算机网络的分类了解计算机网络的组成 2.2 计算机网络的定义 2.3 计算机网络的功能 2.4 计算机网络的分类 物理拓扑结构分类&#xff1a;总线型、环型、星型 2.5 计算机网络的组成 网络适配器(NIC)接口规格分类&a…

AI大模型探索之路-训练篇21:Llama2微调实战-LoRA技术微调步骤详解

系列篇章&#x1f4a5; AI大模型探索之路-训练篇1&#xff1a;大语言模型微调基础认知 AI大模型探索之路-训练篇2&#xff1a;大语言模型预训练基础认知 AI大模型探索之路-训练篇3&#xff1a;大语言模型全景解读 AI大模型探索之路-训练篇4&#xff1a;大语言模型训练数据集概…

DRF渲染之异常处理

异常处理 【1 】引言 Django REST Framework 这个就是我们常常说的DRF APIView的dispatch方法&#xff1a; 当请求到达视图时&#xff0c;DRF 的 APIView 类会调用 dispatch 方法来处理请求。在 dispatch 方法中&#xff0c;有一个关键的步骤是处理异常。如果在视图类的方法…

BGP综合大实验

实验要求 1.AS1中存在两个环回&#xff0c;一个地址是192.168.1.0/24&#xff0c;改地址不能在任何协议中宣告&#xff1b;AS3中存在两个环回&#xff0c;一个地址为192.168.2.0/24&#xff0c;该地址不能在任何协议中宣告&#xff0c;最终要求这两个环回可以ping通&#xff1b…

AI绘画动漫转真人详细教程

从小到大&#xff0c;我们看过的动漫、玩过的游戏有很多很多 但我们会发现里面的角色或者人物都是二次元的 我就会好奇这些动漫人物在现实中会长什么样 而现在&#xff0c;我们通过AI绘画竟然就能还原出来他们现实中的样子 除了动漫角色和游戏人物&#xff0c;古代的画像、经典…

K-AI01,K-AO01,K-BUS02和利时

K-AI01,K-AO01,K-BUS02和利时1.将工程师站的计算机开机&#xff1b;2.开机后鼠标双击桌面上的“maintenance tool”图标&#xff0c;K-AI01,K-AO01,K-BUS02和利时。出现如下图标&#xff1a; 按顺序点击”图标中的箭头所指的按钮&#xff0c;出现如下画面选中画面中需要强制的逻…

C++语言题库(三)—— PAT

目录 1. 打印点、圆、圆柱信息 2. 国际贸易统计 3. 设计一个类CRectangle 4. 定义一个时间类 5. 定义一个Date类 6. 定义一个Time类 7. 设计一个People类 8. 平均成绩 9. 计算若干个学生的总成绩及平均成绩 11. 使用面向对象的方法求长方形的周长 1. 打印点、圆、圆柱…