《数据结构》--栈【概念应用、图文并茂】
本节讲完栈下次再讲一下队列,最后补充一个串,我们的线性结构基本就完事了。下图中黄色框框圈中的是我们今日份内容(分为两篇博客):
知识体系图
栈(Stack-LIFO)结构
栈的基础概念
栈(Stack)是一个后进先出(Last-In-First-Out)的一个特殊数据结构,只有一端可以操作,三面环墙。按照存储方式可以分为顺序存储的栈和链式存储的栈(即顺序栈和链栈),另外还有一些特殊的栈需要我们了解:单调栈、共享栈。
不管什么栈,它的基本API接口(基本操作)都是:入栈(Push),出栈(Pop),查看栈顶元素(Top/Peek),判断栈是否为空(isEmpty)。
栈顶(top):处于可以插入或删除元素的一端,栈顶指针指向非空元素的下一个位置
栈底(base):处于不能插入或删除元素的另一端,栈底指针固定指向。
空栈(empty):栈内不包含任意元素,栈顶指针与栈底指针指向同一位置。
顺序栈
采用顺序结构存储的栈称为顺序栈,它的底层是数组,使用的是一段地址连续的空间。另外找一个尾指针充当栈顶指针,指向栈顶元素的位置的下一个地址。判断栈为空的条件就是栈顶指针指向栈底。
1.栈的顺序存储
站的顺序存储结构可以定义为:一个栈底指针,一个栈顶指针
#define INIT_MAX_SIZE 20 //初始化栈时给的最大容量
typedef int DataType;//DataType的类型不确定,根据习惯初始假定为int
typedef struct{DataType* base;DataType* top;
}SqStack;
2.栈的基本操作
初始化
初始化时,使用预先设置好的初值来开辟大小,并且初始一定是空栈,所以让栈顶指针指向栈底即可。
void initStack(SqStack* s)
{s->base = (DataType*)malloc(sizeof(DataType)*INIT_MAX_SIZE);s->top = s->base;
}
判空
如果栈顶指针与栈底指针指向同一块空间,那么栈就是空的,直接返回。这里传不传指针都可以,不需要进行修改的操作,只进行判断,所以利用值传递也能实现目的
#include <stdbool.h>
bool isEmpty(SqStack s){return s.base == s.top;
}
bool isEmpty(SqStack* s){return s->base == s->top;
}
进栈
进栈之前,我们先判断栈是否为满,如果满栈,我们不能随便加入,需要扩容,我们选择二倍扩容的方式,如果扩容失败,结束程序,一旦我们扩容成功,我们原来的栈顶指针也会失效,使用栈底指针加上原来的满栈数,跨越总步长回到栈顶的相对位置。然后将栈顶设置为元素e,并自动跨越一个步长(++)。
/*插入元素e为新的栈顶元素*/
void Push(SqStack *s, DataType e){//满栈if(s->top == s->base + INIT_MAX_SIZE){int size = INIT_MAX_SIZE*2;s->base = (DataType*)realloc(s->base,sizeof(DataType)*size);assert(s->base);s->top = s->base + size/2;}*(s->top) = e; //将新插入元素赋值给栈顶空间s->top++; //更改栈顶指针//可以合并为*(s->top++)=e;
}
出栈
出栈就是要后面的元素无法访问,那么只需要将栈顶指针缩减一个步长(--)即可,但在此之前,我们需要判断是不是空栈,如果是空栈,我们就没必要去进行出栈的操作了。
/*若栈不空,则删除S的栈顶元素*/
void Pop(SqStack *s){if(s->top == s->base){return;}S->top--; //栈顶指针减1
}
获取栈顶元素
获取栈顶元素,我们需要获取的栈顶指针上一个位置内存储的内容,所以我们进行完判空操作后,我们需要对s->top-1进行解引用(即取*)的操作。
/*读栈顶元素*/
DataType Top(SqStack s){if(s->top == s->base){ //栈空return -1;}return *(s->top-1); //返回栈顶元素
}
销毁栈
void destory(SqStack* s){if(s->base){free(s->base);s->top=s->base=NULL;}
}
3.单调栈专题
从名字上我们就可以看出来,单调栈中存放的数据应该是有序的,所以单调栈本身也应该分为单调递增栈和单调递减栈。单调的方向是从栈底到栈顶。
在进行单调栈的讲解时,我们会使用到上述的一些基本操作。我们来看一下单调栈的入栈过程:
typedef int DataType;//此处选取int作为示例for (遍历这个数组)
{if (栈空 || 栈顶元素大于等于当前比较元素){入栈;}else{while (栈不为空 && 栈顶元素小于当前元素){栈顶元素出栈;更新结果;}当前数据入栈;}
}
真题演练
柱状图中的最大矩形
思路:当前的数字可以向两边拓展,遇到比自己大的就接着拓展,小的就停止,然后用自己的高度乘以拓展的宽度,每次都更新最大面积,时间复杂度同样为O(N^2),所以我们接着借助单调栈
这里我们通过这道例题来使用一下单调递减栈
1.设置一个单调递减的栈(栈内0~n为单调递增)
2.当遇到小于栈顶元素的值,我们开始更新数据,因为有可能最大面积就会出现在栈中的序列里
3.牢记栈中数据永远是有序的,这个问题比较复杂,所以读者不妨对照着代码来理解问题
int largestRectangleArea(vector<int>& heights) {heights.push_back(-1);/同理,我们希望栈中所有数据出栈,所以给数组最后添加一个负数SqStack st; initStack(&st);int ret = 0, top;for (int i = 0; i < heights.size(); i++){if (isEmpty(st) || heights[Top(st)] <= heights[i]){Push(&st,i);}else{while (!isEmpty(st) && heights[Top(st)] > heights[i]){top = Top(st);Pop(&st);//i-top指的是当前矩形的宽度,heights[top]就是当前的高度//再次强调栈中现在为单调递增int tmp = (i - top)*heights[top];if (tmp > ret)ret = tmp;}Push(top);heights[top] = heights[i];}}return ret;
}
4.共享栈专题
利用栈底位置相对不变的特征,可让两个顺序栈共享一个一维数组空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸,如下图所示:
两个栈共享同一片存储空间,这片存储空间不单独属于任何一个栈,某个栈需要的多一点,它就可能得到更多的存储空间;两个栈的栈底在这片存储空间的两端,当元素入栈时,两个栈的栈顶指针相向而行。此时我们采取另外一种实现栈的方法,不使用真正的指针,使用下标索引模拟两个栈顶指针。定义方法如下:
#define SharedStackMax 100typedef char SharedStackType;typedef struct{SharedStackType data[SharedStackMax];size_t top1;size_t top2;}SharedStack;
共享栈的基本操作方法:
void SharedStackInit(SharedStack*stack){if(stack==NULL){return;}stack->top1=0;stack->top2=SharedStackMax;}void SharedStackPush1(SharedStack*stack,SharedStackType value){if(stack->top1==stack->top2||stack==NULL){return;}stack->data[stack->top1++]=value;return;}void SharedStackPush2(SharedStack*stack,SharedStackType value){if(stack->top2==stack->top1||stack==NULL){return;} stack->data[--stack->top2]=value;}int SharedStackTop1(SharedStack*stack,SharedStackType*value){if(stack==NULL||value==NULL){return 0;}if(stack->top1==0){return 0;}*value=stack->data[top1-1];return 1;}int SharedStackTop2(SharedStack*stack,SharedStackType*value){if(stack==NULL||value==NULL){return 0;}if(stack->top2==SharedStackMax){return 0;}*value=stack->data[stack->top2];return 1;
}void SharedStackPop1(SharedStack*stack){if(stack==NULL || stack->top1==0){return;}stack->top1--;}void SharedStackPop2(SharedStack*stack){if(stack->top2==SharedStackMax || stack==NULL){return;}stack->top2++;}
链栈
1.栈的链式存储
采用链式存储的栈称为链栈,链栈的优点是便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况,我们就省去了不断扩容的麻烦。通常采用不带头结点的单链表实现,而且为了方便,使用头插法头删法更简单,所以我们的入栈和出栈就是单链表的头插和头删的操作。Lhead指向栈顶元素,链栈的结构图如下所示:
对于链栈来说,空栈的概念即是Lhead指向NULL的时候。
/*构造节点*/
typedef struct StackNode{ElemType data;struct StackNode *next;
}StackNode, *LinkStackPrt;
//将StackNode* 重命名为LinkStackPrt的好处是避免下面操作出现二级指针的形式。(实质还是二级指针)/*构造链栈*/
typedef struct LinkStack{LinkStackPrt top;int count;
}LinkStack;
2.链栈的基本操作
链栈的进栈
对于链栈的进栈push操作,我们会创建一个栈节点,然后进行链表的头插操作。
void Push(LinkStack *S, ElemType e){LinkStackPrt p = (LinkStackPrt)malloc(sizeof(StackNode));p->data = e;p->next = S->top; //把当前的栈顶元素赋值给新节点的直接后继S->top = p; //将新的结点S赋值给栈顶指针S->count++;
}
链栈的出栈
无需多说,头删即可
Element Pop(LinkStack *S){if(StackEmpty(*S)){return -1;}Element e = S->top->data;//临时存储栈顶元素LinkStackPtr p;p = S->top; //将栈顶结点赋值给pS->top = S->top->next; //使得栈顶指针下移一位,指向后一结点free(p); //释放结点pS->count--;return e;
}
栈的算法应用
递归专题
我们将递归时就提到过,递归是函数的递进调用与返回,而函数的调用与销毁又是以栈的形式进行的。所以能用栈解决的问题一定可以用递归解决。而能用递归的方式解决的问题,栈不一定能解决,但一定依托于栈的形式结构。因为对于栈结构本身来讲是作为一个容器来用的,递归才是存储一系列操作这种非数据类型的特殊栈结构。
基础算法--递归算法【难点、重点】-CSDN博客
调用堆栈:每次递归调用时,栈上会创建一个新的堆栈帧,用于存储该调用的上下文。
空间复杂度:递归的深度可能导致栈溢出,尤其在递归深度较大而没有适当的基本情况时。
迭代替代:有些递归算法可以用显式栈或循环来实现,从而避免递归带来的栈溢出问题。
四则运算表达式专题
四则运算在这里的应用其实包括后缀表达式和前缀表达式,以及我们平常熟悉的中缀表达式。
缀所处的位置不同代表的含义是四则运算符所处的位置不同。例如:
a+b*(c/d+2):中缀表达式
* + a b c:前缀表达式( ==(a+b)*c )
a b c d - * + e f / -:后缀表达式( ==a+b*(c-d)-e/f )
四则运算表达式的专题我们以一道例题作为训练:逆波兰表达式(前缀表达式)。
逆波兰表达式是一种把运算符前置的算术表达式,例如普通的表达式2 + 3的逆波兰表示法为+ 2 3。逆波兰表达式的优点是运算符之间不必有优先级关系,也不必用括号改变运算次序,例如(2 + 3) * 4的逆波兰表示法为* + 2 3 4。本题求解逆波兰表达式的值,其中运算符包括+ - * /四个。
对于原逆波兰表达式:- + * - c d b a / e f
当我们遇到符号时,将其入栈,等到下面两个是数字时再出栈,如果遇到数字,直接让其入栈,然后出栈。我们对下面的图进行详细的步骤分析:
文字配合图片食用效果更甚哦:
下面给出代码:
#include <bits/stdc++.h>
using namespace std;/*逆波兰表达式*/
string st = "+-*/";
double calc() {string str; cin >> str;switch (str[0]) {case '+':return calc() + calc();case '-':return calc() - calc();case '*':return calc() * calc();case '/':return calc() / calc();default:return stod(str);}
}
signed main() {printf("%f\n", calc());return 0;
}
感谢大家观看!多多支持哦!
相关文章:

《数据结构》--栈【概念应用、图文并茂】
本节讲完栈下次再讲一下队列,最后补充一个串,我们的线性结构基本就完事了。下图中黄色框框圈中的是我们今日份内容(分为两篇博客): 知识体系图 栈(Stack-LIFO)结构 栈的基础概念 栈(Stack)是一个后进先出(Last-In-First-Out)的一个特殊数据…...

国外电商系统开发-运维系统文件下载
文件下载,作者设计的比较先进,如果下载顺利,真的还需要点两次鼠标,所有的远程文件就自动的下载到了您的PC电脑上了。 现在,请您首选选择要在哪些服务器上下载文件: 选择好了服务器以后,现在选择…...

【CSS in Depth 2 精译_045】7.1 CSS 响应式设计中的移动端优先设计原则(上)
当前内容所在位置(可进入专栏查看其他译好的章节内容) 第一章 层叠、优先级与继承(已完结) 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位(已完结) 2.1 相对…...

在线教育新篇章:SpringBoot系统开发策略
2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…...

cmdsh
#!/bin/bash #set -x bindirname "$0" bincd "$bin"; pwd echo $bin if [ $# -lt 2 ] then echo “Usage: ./runRemoteCmd.sh Command MachineTag” echo “Usage: ./runRemoteCmd.sh Command MachineTag confFile” exit fi cmd$1 tag$2 if [ a’ 3 ′…...

一键生成PPT的AI工具-Kimi!
一键生成PPT的AI工具-Kimi! 前言介绍Kimi为什么选择Kimi如何使用Kimi在线编辑PPT下载生成的PPT自己编辑 结语 😀大家好!我是向阳🌞,一个想成为优秀全栈开发工程师的有志青年! 📔今天不来讨论前后…...

java.lang.NoClassDefFoundError: kotlin/Result解决方案
问题 在控制窗口上虽然报错是找不到对应的class,但是呢在我们导入kotlin的后,还是报相同的异常,在网上查找了各种资料,都没有解决方案。 问题分析 在idea2021之后,kotlin都使用远程仓库(kotlinx-coeouti…...

LSTM的变体
一、GRU 1、什么是GRU 门控循环单元(GRU)是一种循环神经网络(RNN)的变体,它通过引入门控机制来控制信息的流动,从而有效地解决了传统RNN中的梯度消失问题。GRU由Cho等人在2014年提出,它简化了…...

LeetCode讲解篇之852. 山脉数组的峰顶索引
文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们可以采用二分查找,每次查询区间中点元素与中点下一个元素比较 如果中点元素大于其下一个元素,则表示从中点开始向右是递减趋势,那峰值索引一定小于等于中点,我…...

矿井人员数据集,用于目标检测,深度学习,采用txt打标签,即yolo格式,也有原文件可以自己转换。总共3500张图片的数据量,划分给训练集2446张,
矿井人员数据集,用于目标检测,深度学习,采用txt打标签,即yolo格式,也有原文件可以自己转换。总共3500张图片的数据量,划分给训练集2446张: ### 矿井人员数据集用于目标检测的详细说明 #### 1. …...

消息队列RabbitMQ
文章目录 1. 简介与安装2. 基本概念3. SpringAMQP4. 交换机类型5. 消息转换器5.1 默认转换器5.2 配置JSON转换器 6 生产者的可靠性6.1 生产者超时重连机制6.2 生产者确认机制 6. MQ的可靠性6.1 数据持久化6.2 惰性队列 Lazy Queue 7. 消费者的可靠性7.1 消费者确认机制7.2 失败…...

RabbitMQ概述
什么是MQ MQ (message queue)消息队列 MQ从字⾯意思上看,本质是个队列,FIFO先⼊先出,只不过队列中存放的内容是消息(message).消息可以⾮常简单,⽐如只包含⽂本字符串,JSON等,也可以很复杂,⽐如内嵌对象 RabbitMQ是MQ的一种实现,是Rabbit 企业下的⼀个消息队列产…...

Golang学习路线
以下是一条学习Golang(Go语言)的路线: 一、基础入门 1. 环境搭建 安装Go编译器,在官网(https://golang.org/dl/)下载适合操作系统的安装包并配置好环境变量。 2. 语法学习学习变量、数据类型(…...

Flink从ck拉起任务脚本
#!/bin/bashAPP_NAME"orderTest"CHECKPOINT_BASE_PATH"hdfs:///jobs/flink/checkpoints/aaa-test/"is_running$(yarn application -list | grep -w "$APP_NAME" | grep -c "RUNNING")if [ $is_running -gt 0 ]; thenecho "应用程…...

GADBench Revisiting and Benchmarking Supervised Graph Anomaly Detection
Neurips 23 推荐指数: #paper/⭐⭐⭐ 领域:图异常检测 胡言乱语: neurips 的benchmark模块的文章总能给人一些启发性的理解,这篇的insight真有意思。个人感兴趣的地方会加粗。此外,这篇文章和腾讯AIlab合作ÿ…...

某象异形滑块99%准确率方案
注意,本文只提供学习的思路,严禁违反法律以及破坏信息系统等行为,本文只提供思路 如有侵犯,请联系作者下架 该文章模型已经上线ocr识别网站,欢迎测试!!,地址:https://yxlocr.windy-rain.cn/ocr/slider/6 所谓的顶象异形滑块,是指没有采用常规的缺口,使用各种形状的…...

CDN绕过学习
1.什么是CDN? CDN就是分布在各个地区的服务器,这些服务器储存着数据的副本。 哪些服务器比较接近你,当你发起请求时,提前就会快速为你提供服务。 总结来说就是: 其实就是用来加速访问的,以及缓解压力&a…...

SpringBoot日常:redission的接入使用和源码解析
文章目录 一、简介二、集成redissionpom文件redission 配置文件application.yml文件启动类 三、JAVA 操作案例字符串操作哈希操作列表操作集合操作有序集合操作布隆过滤器操作分布式锁操作 四、源码解析 一、简介 Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格…...

npm包管理深度探索:从基础到进阶全面教程!
目录 一、npm概述1、npm简介(1)什么是npm?(2)npm的核心功能(3)npm的工作原理(4)npm的优势(5)npm的局限性(6)总结 2、npm的…...

最新免费GPT4O和Midjourney
一、什么是GPT4O? GPT-4 是 OpenAI 研发的大型语言模型。它具有强大的语言理解和生成能力,在自然语言处理等诸多领域有着广泛的应用和表现。 二、什么是Midjourney? Midjourney 是一款人工智能图像生成工具。它可以根据用户输入的描述或提…...

python操作OpenAI教程
python操作OpenAI pip install -U openai代码: from openai import OpenAI# 解决请求超时问题 import os os.environ["http_proxy"] "http://localhost:7890" os.environ["https_proxy"] "http://localhost:7890"# 需要…...

如何版本REST API:综合指南
目录 总则什么是REST API版本控制?为什么API版本控制很重要?如何对REST API进行版本控制 理解API契约评估需求选择版本控制策略沟通变化保持向后兼容性详细文档记录REST API版本控制最佳实践REST API版本控制常见问题:REST API版本控制总则 版本化REST API对于确保软件应用…...

Docker 环境下 Nginx 监控实战:使用 Prometheus 实现 Nginx 性能监控的完整部署指南
Docker 环境下 Nginx 监控实战:使用 Prometheus 实现 Nginx 性能监控的完整部署指南 文章目录 Docker 环境下 Nginx 监控实战:使用 Prometheus 实现 Nginx 性能监控的完整部署指南一 查看模块是否安装二 配置 status 访问端点三 Docker 部署 nginx-prome…...

网络安全-IPv4和IPv6的区别
1. 2409:8c20:6:1135:0:ff:b027:210d。 这是一个IPv6地址。IPv6(互联网协议版本6)是用于标识网络中的设备的一种协议,它可以提供比IPv4更大的地址空间。这个地址由八组十六进制数字组成,每组之间用冒号分隔。IPv6地址通常用于替代…...

【移动端】事件基础
一、移动端事件分类 移动端事件主要分为以下几类: 1. 触摸事件(Touch Events) 触摸事件是移动设备特有的事件,用来处理用户通过触摸屏幕进行的操作。主要的触摸事件有: touchstart:手指触摸屏幕时触发。…...

软件测试比赛-学习
一、环境配置 二、浏览器适配 //1.设置浏览器的位置,google浏览器位置是默认且固定在电脑里的//2.设置浏览器驱动的位置,C:\Users\27743\AppData\Local\Google\Chrome\ApplicationSystem.setProperty("webdriver.chrome.driver", "C:\\Users\\27743\\AppData\\…...

力扣LeetCode-链表中的循环与递归使用
标题做题的时候发现循环与递归的使用差别: 看两道题: 两道题都是不知道链表有多长,所以需要用到循环,用到循环就可以把整个过程分成多个循环体,就是每一次循环要执行的内容。 反转链表: 把null–>1…...

AFSim仿真系统 --- 系统简解_08 传感器与特征
传感器与特征 传感器是平台的一部分,为拥有该平台提供了探测其他平台及其组成部分的能力。 特征是平台的一种属性,用于确定特定传感器是否能够探测到特征所拥有的平台。 以下是用于探测平台的一些特征属性列表: 声学红外光学雷达 AFSIM …...

已经安装了qt,想添加mingw组件,包含gcc等
1、已经安装了qt,想添加mingw组件, 步骤1 双击打开MaintenanceTool.exe, 步骤2: 选择清华大学开源软件镜像网站,选择相应QT版本添加网址https://mirrors.tuna.tsinghua.edu.cn/qt/online/qtsdkrepository/windows_x8…...

数据库管理-第250期 深入浅出多主多活数据库技术- Cantian存储引擎(一)(20241009)
数据库管理250期 2024-10-09 数据库管理-第250期 深入浅出多主多活数据库技术- Cantian存储引擎(一)(20241009)1 简介2 引擎构成3 引擎架构4 文件分布5 分布式MVCC6 限制/要求总结 数据库管理-第250期 深入浅出多主多活数据库技术…...