数据结构:栈(stack)详解 c++信息学奥赛基础知识讲解

news2024/10/9 12:25:12

目录

一、栈的定义

二、栈的操作

三、代码实操

四、栈的实现

1、string实现stack

2、vector实现stack

3、deque实现栈


一、栈的定义

stack是一个比较简单易用的数据结构,stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。遵循的是后进先出的原则、Last In Fist Out,LIFO(跟队列是反的,栈是后进先出)

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

如何声明一个栈

stack<储存的类型> 容器名

常规类型栈

  • 储存int型数据的栈 stack<int> s;
  • 储存double型数据的栈 stack<double> s;
  • 储存string型数据的栈 stack<string> s;
  • 储存结构体或者类的栈 stack<结构体名> s;

数组栈stack:

  • 储存int型数据的栈 stack<int> s[n];
  • 储存double型数据的栈 stack<double> s[n];
  • 等等,n为数组的大小

二、栈的操作

//其实栈就这几个成员函数
push() //在栈顶增加元素 
pop() //移除栈顶元素 
top() //返回栈顶元素 
empty() //堆栈为空则返回真 
size() //返回栈中元素数目 

三、代码实操

#include<iostream>//c++标准头文件,可以使用cout,cin等标准库函数 
#include<stack>//使用stack时需要的头文件 
using namespace std;//命名空间,防止重名给程序带来各种隐患,使用cin,cout,stack,map,set,vector,queue时都要使用
int main(){
	stack<int> s;//定义一个int类型的stack
	
	s.push(1);//往栈里放入一个元素1
	s.push(2);//往栈里放入一个元素2
	s.push(3); //往栈里放入一个元素3
	
	cout<<"按顺序放入元素1、2、3后,目前栈里的元素:1 2 3" <<endl;
	cout<<"s.size()="<<s.size()<<endl;//s.size()返回栈内元素的个数  
	cout<<"s.empty()="<<s.empty()<<endl; //判断栈是否为空,值为1代表空,0代表非空,用s.size()同样可以判断 ,s.size()的值为0就代表空的 
	cout<<"s.top()="<<s.top()<<endl;//查看栈顶的元素 
	cout<<endl;
	
	s.pop();//弹出栈顶元素 
	cout<<"s.pop()后,目前栈里的元素:1 2"<<endl;
	cout<<"s.size()="<<s.size()<<endl;
	cout<<"s.empty()="<<s.empty()<<endl; 
	cout<<"s.top()="<<s.top()<<endl;
	cout<<endl;
	
	
	s.pop();
	cout<<"s.pop()后,目前栈里的元素:1"<<endl;
	cout<<"s.size()="<<s.size()<<endl;
	cout<<"s.empty()="<<s.empty()<<endl; 
	cout<<"s.top()="<<s.top()<<endl;
	cout<<endl;
	 
	s.pop();
	cout<<"s.pop()后,目前的栈是空的"<<endl;
	cout<<"s.size()="<<s.size()<<endl;
	cout<<"栈是空的就不能用s.top()访问栈顶元素了" <<endl; 
	cout<<"s.empty()="<<s.empty()<<endl; 
	 
}

运行结果

按顺序放入元素1、2、3后,目前栈里的元素:1 2 3
s.size()=3
s.empty()=0
s.top()=3

s.pop()后,目前栈里的元素:1 2
s.size()=2
s.empty()=0
s.top()=2

s.pop()后,目前栈里的元素:1
s.size()=1
s.empty()=0
s.top()=1

s.pop()后,目前的栈是空的
s.size()=0
栈是空的就不能用s.top()访问栈顶元素了
s.empty()=1

四、栈的实现

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如string、vector和list都可以

1、string实现stack

栈中的数据是不允许随机访问的,就是不能像数组那样用下标访问,也不能遍历栈内的元素,这是很局限的。实际上,我们经常使用的string类型就是一种栈结构,但是我们可以通过下标访问元素,我们可以看看

//string的栈相关的成员函数
empty() //堆栈为空则返回真 
pop_back() //移除栈顶元素 
push_back() //在栈顶增加元素 
size() //返回栈中元素数目 
back() //返回栈顶元素 

示例代码:

#include<iostream>//c++标准头文件,可以使用cout,cin等标准库函数 
#include<string>//string包括了一些字符串操作的库函数,但用string时是不用引入这个头文件的
using namespace std;//命名空间,防止重名给程序带来各种隐患,使用cin,cout,stack,map,set,vector,queue时都要使用
int main(){
	string s;//定义一个字符串
	
	s.push_back('1');//往栈里放入一个元素1
	s.push_back('2');//往栈里放入一个元素2
	s.push_back('3'); //往栈里放入一个元素3
	
	cout<<"按顺序放入字符1、2、3后,目前string里的元素:" ;
	for(int i=0;i<s.size();i++){
		cout<<s[i]<<' ';
	}
	cout<<endl; 
	cout<<"s.pop_back()="<<s.size()<<endl;//s.size()返回string内字符的个数  
	cout<<"s.empty()="<<s.empty()<<endl; //判断栈是否为空,值为1代表空,0代表非空,用s.size()同样可以判断 ,s.size()的值为0就代表空的 
	cout<<"s.back()="<<s.back()<<endl;//查看栈顶的元素 
	cout<<endl;
	
	s.pop_back();//弹出栈顶元素 
	cout<<"s.pop_back()后,目前string里的元素:";
	for(int i=0;i<s.size();i++){//可以通过下标随机访问元素 
		cout<<s[i]<<' ';
	}
	cout<<endl; 
	cout<<"s.size()="<<s.size()<<endl;
	cout<<"s.empty()="<<s.empty()<<endl; 
	cout<<"s.back()="<<s.back()<<endl;
	cout<<endl;
	
	s.pop_back();
	cout<<"s.pop_back()后,目前string里的元素:";
	for(int i=0;i<s.size();i++){
		cout<<s[i]<<' ';
	}
	cout<<endl; 
	cout<<"s.size()="<<s.size()<<endl;
	cout<<"s.empty()="<<s.empty()<<endl; 
	cout<<"s.back()="<<s.back()<<endl;
	cout<<endl;
	 
	s.pop_back();
	cout<<"s.pop_back()后,目前的string是空的"<<endl;
	cout<<"s.size()="<<s.size()<<endl;
	cout<<"string是空的就不能用s.back()访问栈顶元素了" <<endl; 
	cout<<"s.empty()="<<s.empty()<<endl; 
	 
}

输出结果:

按顺序放入字符1、2、3后,目前string里的元素:1 2 3
s.pop_back()=3
s.empty()=0
s.back()=3

s.pop_back()后,目前string里的元素:1 2
s.size()=2
s.empty()=0
s.back()=2

s.pop_back()后,目前string里的元素:1
s.size()=1
s.empty()=0
s.back()=1

s.pop_back()后,目前的string是空的
s.size()=0
string是空的就不能用s.back()访问栈顶元素了
s.empty()=1

2、vector实现stack

vector是stack的升级版,多了很多成员函数,像随机插入函数insert()等等,大家完全可以用vector替代stack的。vector和string不同的是,string只能存储char类型的,而vector能存储所有类型的数据,想int、double、结构体、类等等

//vector的栈相关的成员函数
empty() //堆栈为空则返回真 
pop_back() //移除栈顶元素 
push_back() //在栈顶增加元素 
size() //返回栈中元素数目 
back() //返回栈顶元素 

示例代码:

#include<iostream>//c++标准头文件,可以使用cout,cin等标准库函数 
#include<vector>//使用vector时需要的头文件 
using namespace std;//命名空间,防止重名给程序带来各种隐患,使用cin,cout,stack,map,set,vector,queue时都要使用
int main(){
	vector<int> v;//定义一个int类型的stack
	
	v.push_back(1);//往vector里放入一个元素1
	v.push_back(2);//往vector里放入一个元素2
	v.push_back(3); //往vector里放入一个元素3
	
	cout<<"按顺序放入字符1、2、3后,目前vector里的元素:" ;
	for(int i=0;i<v.size();i++){//可以通过下标随机访问元素 
		cout<<v[i]<<' ';
	}
	cout<<endl; 
	cout<<"v.pop_back()="<<v.size()<<endl;//v.size()返回vector内元素的个数  
	cout<<"v.empty()="<<v.empty()<<endl; //判断栈是否为空,值为1代表空,0代表非空,用v.size()同样可以判断 ,v.size()的值为0就代表空的 
	cout<<"v.back()="<<v.back()<<endl;//查看栈顶的元素 
	cout<<endl;
	
	v.pop_back();//弹出栈顶元素 
	cout<<"v.pop_back()后,目前vector里的元素:";
	for(int i=0;i<v.size();i++){
		cout<<v[i]<<' ';
	}
	cout<<endl; 
	cout<<"v.size()="<<v.size()<<endl;
	cout<<"v.empty()="<<v.empty()<<endl; 
	cout<<"v.back()="<<v.back()<<endl;
	cout<<endl;
	
	v.pop_back();
	cout<<"v.pop_back()后,目前vector里的元素:";
	for(int i=0;i<v.size();i++){
		cout<<v[i]<<' ';
	}
	cout<<endl; 
	cout<<"v.size()="<<v.size()<<endl;
	cout<<"v.empty()="<<v.empty()<<endl; 
	cout<<"v.back()="<<v.back()<<endl;
	cout<<endl;
	
	v.pop_back();
	cout<<"v.pop_back()后,目前的vector是空的"<<endl;
	cout<<"v.size()="<<v.size()<<endl;
	cout<<"vtring是空的就不能用v.back()访问栈顶元素了" <<endl; 
	cout<<"v.empty()="<<v.empty()<<endl; 
}

输出结果:

按顺序放入字符1、2、3后,目前vector里的元素:1 2 3
v.pop_back()=3
v.empty()=0
v.back()=3

v.pop_back()后,目前vector里的元素:1 2
v.size()=2
v.empty()=0
v.back()=2

v.pop_back()后,目前vector里的元素:1
v.size()=1
v.empty()=0
v.back()=1

v.pop_back()后,目前的vector是空的
v.size()=0
vtring是空的就不能用v.back()访问栈顶元素了
v.empty()=1

3、deque实现栈

deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。

deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:

deque 折中了 vector 与 string的结构,使用多个小数组来存储空间,为了管理这些小数组,又开辟了一个叫做中控的指针数组,数组中的指针分别指向这些小数组。

 需要注意的是,最开始使用指针是中控指针数组中间位置的指针,当进行头插、尾插的时候,就可以直接使用前一个、后一个指针指向新开辟的空间了:

当中控数组满时,只需对中控数组进行扩容就可以了, 而且由于中控数组中存放的都是指针,所以拷贝代价极低。

由以上结构我们可知deque的优点有:

  1. 相比vector,deque 的扩容代价低
  2. 头插头删、尾插尾删效率高
  3. 支持下标随机访问

比如,假设每个小数组的容量为 10 ,我们想要找到下标为 25 的元素,只需要用下标减去第一个数组内元素的个数,再除以每个数组的容量就能找到其所在哪一个小数组。对应到上面我们所画的图中,就是 (25 - 1) / 10 。找到对应元素存在于第 2 个数组后,再用 (25 - 1) % 10 就可以知道对应元素是在该小数组中的第几个。

综上:

  1. 与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
  2. 与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构。

STL中对stack和queue默认选择deque作为其底层容器,主要是因为:

  1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。
  2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。结合了deque的优点,而完美的避开了其缺陷。

deque的迭代器

五、案例实操

题目描述:实现一个MyQueue类,该类用两个栈来实现一个队列。

示例:

MyQueue queue = new MyQueue();

queue.push(1);
queue.push(2);
queue.peek();  // 返回 1
queue.pop();   // 返回 1
queue.empty(); // 返回 false

说明:

  • 你只能使用标准的栈操作 -- 也就是只有 push to toppeek/pop from topsize 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。

代码实现:

class MyQueue {
private:
    stack<int> inStack, outStack;
    //私有成员函数入栈并出栈
    void in2out() {
        while (!inStack.empty()) {
            outStack.push(inStack.top());
            inStack.pop();
        }
    }

public:
    MyQueue() {}
    //入队
    void push(int x) {
        inStack.push(x);
    }
    //出队并返回首元素
    int pop() {
        if (outStack.empty()) {
            in2out();
        }
        int x = outStack.top();
        outStack.pop();
        return x;
    }
    //返回队首元素
    int peek() {
        if (outStack.empty()) {
            in2out();
        }
        return outStack.top();
    }
    //判断队是否为空
    bool empty() {
        return inStack.empty() && outStack.empty();
    }
};

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

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

相关文章

泰勒斯威夫特2022年纽约大学毕业典礼演讲:NYU‘s 2022 Commencement Speaker Taylor Swift

NYU’s 2022 Commencement Speaker Taylor Swift Link: https://www.youtube.com/watch?vOBG50aoUwlI Singer, songwriter, producer, and director Taylor Swift received a Doctor of Fine Arts, honoris causa, at the Commencement for the Class of 2022 and delivered …

Java | Leetcode Java题解之第198题打家劫舍

题目&#xff1a; 题解&#xff1a; class Solution {public int rob(int[] nums) {if (nums null || nums.length 0) {return 0;}int length nums.length;if (length 1) {return nums[0];}int first nums[0], second Math.max(nums[0], nums[1]);for (int i 2; i <…

图形编辑器基于Paper.js教程04: Paper.js中的基础知识

背景 了解paper.js的基础知识&#xff0c;在往后的开发过程中会让你如履平地。 基础知识 paper.js 提供了两种编写方式&#xff0c;一种是纯粹的JavaScript编写&#xff0c;还有一种是使用官方提供的PaperScript。 区别就是在于&#xff0c;调用paper下的字对象是否需要加pa…

吉他练琴软件哪个好 Guitar Pro如何辅助练琴

在现代音乐学习和创作中&#xff0c;吉他打谱软件的作用越来越为人们所重视。随着技术的不断发展&#xff0c;各种吉他练琴软件也如雨后春笋般涌现&#xff0c;为吉他爱好者提供了更多选择。下面我们来看看吉他练琴软件哪个好&#xff0c;Guitar Pro如何辅助练琴的相关内容。 一…

出现 defineProps is a compiler macro and no longer needs to be imported. 解决方法

目录 1. 问题所示2. 原理分析3. 解决方法1. 问题所示 执行前端代码的时候,出现如下问题: [@vue/compiler-sfc] defineProps is a compiler macro and no longer needs to be imported.[@vue/compiler-sfc] defineEmits is a compiler macro and no longer needs to be impo…

在线客服源码系统全端通用 源码完全开源可以二次开发 带完整的安装代码包以及搭建教程

系统概述 在线客服源码系统采用了先进的技术架构&#xff0c;包括前端界面、后端服务、数据库等部分。前端界面采用了响应式设计&#xff0c;能够自适应不同的设备屏幕尺寸&#xff0c;为用户提供良好的使用体验。后端服务采用了高性能的服务器架构&#xff0c;确保系统的稳定…

【安全审核】音视频审核开通以及计费相关

融云控制台音视频审核入口&#xff1a;音视频审核 1 音视频审核文档&#xff1a;融云开发者文档 1 提示&#xff1a; 开发环境&#xff1a; 免费体验 7 天&#xff08;含 21 万分钟音频流和 420 万张视频审核用量&#xff09;&#xff0c;免费额度用尽后&#xff0c;将关停服务…

echarts 折线图柱状图增加点击事件

单折线图&#xff0c;可以直接监听click事件&#xff08;只有点击到折线才会触发&#xff09; this.chart.on(click, () > {console.log(点击,.s)})但很多时候&#xff0c;我们是要求点击折线图任意位置触发点击事件 而且要注意隐藏折线的操作按钮 this.chart.getZr().on…

C++ | Leetcode C++题解之第191题位1的个数

题目&#xff1a; 题解&#xff1a; class Solution { public:int hammingWeight(uint32_t n) {int ret 0;while (n) {n & n - 1;ret;}return ret;} };

RabbitMQ实践——搭建多人聊天服务

大纲 用户登录创建聊天室监听Stream&#xff08;聊天室&#xff09;发送消息实验登录Tom侧Jerry侧 创建聊天室Jerry侧Tom侧 进入聊天室Jerry侧Tom侧 发送消息Jerry发送消息Jerry侧聊天室Tom侧聊天室 Tom发送消息Jerry侧聊天室Tom侧聊天室 代码工程参考资料 在《RabbitMQ实践——…

LongRAG:增强长上下文大语言模型的检索增强生成

这篇论文的标题是《LongRAG: Enhancing Retrieval-Augmented Generation with Long-context LLMs》&#xff0c;由滑铁卢大学的Ziyan Jiang、Xueguang Ma和Wenhu Chen撰写。论文主要探讨了在传统的检索增强生成&#xff08;RAG&#xff09;框架中存在的一些问题&#xff0c;并提…

Linux基础 - 存储结构与管理硬盘

目录 零. 简介 一. 文件系统 Ubuntu 文件系统结构&#xff1a; 路径: 二. 硬盘管理 零. 简介 Linux 文件系统是一种用于组织和存储文件、目录以及相关数据的架构。 常见的 Linux 文件系统有&#xff1a; Ext4&#xff08;Fourth Extended File System&#xff09;&#…

数据结构:队列详解 c++信息学奥赛基础知识讲解

目录 一、队列概念 二、队列容器 三、队列操作 四、代码实操 五、队列遍历 六、案例实操 题目描述&#xff1a; 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 详细代码&#xff1a; 一、队列概念 队列是一种特殊的线性…

了解负载均衡器

现代系统变得越来越复杂&#xff0c;但这种复杂性确保了处理大量的网络流量和请求。 简单来说&#xff0c;负载均衡器的主要思想就像它的名字一样&#xff0c;它跨服务器提供直接的客户端请求。换句话说&#xff0c;负载均衡器是在多台服务器之间分配网络或应用程序流量的系统…

【系统架构设计师】六、信息系统基础知识(定义|分类|企业信息化系统|生命周期|建设原则|开发方法)

目录 一、信息系统的定义 二、信息系统的分类 三、企业使用的信息化系统 四、信息系统的生命周期 五、信息系统建设原则 六、信息系统的开发方法 6.1 结构化方法 6.2 原型法 6.3 构件化开发方法 6.4 面向服务的方法 6.5 面向对象的方法 6.6 敏捷方法 历年真题考情&#x…

AMSR-E/Aqua L1A 原始观测次数,第 3 版

AMSR-E/Aqua L1A Raw Observation Counts, Version 3 简介 改进后的 V003 AMSREL1A 产品对共同登记参数 A1 和 A2 进行了经验修正&#xff0c;并更新了用于修正 AMSR-E 89 GHz 位置信息的参数文件。因此&#xff0c;第三版 AMSREL1A 数据提高了以下方面的精度&#xff1a;纬度…

【个人博客搭建】(26)发布后端webapi项目

1、选择启动的webapi&#xff0c;右击发布 2、选择左下角的“显示所有设置” 在上一页按钮那边是发布文件夹的目录 地址&#xff0c; 现在界面的就是配置的信息&#xff0c; 配置&#xff1a;Debug、Release 目标框架&#xff1a;我们用的net8.0&#xff0c;就是他&#xff…

Golang | Leetcode Golang题解之第191题位1的个数

题目&#xff1a; 题解&#xff1a; func hammingWeight(num uint32) (ones int) {for ; num > 0; num & num - 1 {ones}return }

C语言 | Leetcode C语言题解之第191题位1的个数

题目&#xff1a; 题解&#xff1a; int hammingWeight(uint32_t n) {int ret 0;while (n) {n & n - 1;ret;}return ret; }

S_LOVE多端恋爱小站小程序源码 uniapp多端

S_LOVE多端恋爱小站小程序源码&#xff0c;采用uniapp多端开发框架进行开发&#xff0c;目前已适配H5、微信小程序版本。 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/89421726 更多资源下载&#xff1a;关注我。