栈:限定仅在表尾(栈顶)进行插入和删除操作的线性表 【后进先出的线性表,LIFO结构】
队列:只允许在一段进行插入操作,而在另一端进行删除操作的线性表
栈
栈顶:允许插入和删除的一端
栈底:另一端
空栈:不含任何数据元素的栈
栈的插入:进栈
栈的删除:出栈
进栈出栈的变化形式
最先进栈的是不是最后出? 不一定
栈对线性表的插入和删除位置进行了限制,并没有对元素进出的时间进行限制
事先进去的元素,也可以出栈,只要保证是栈顶元素就可以出栈
举例:将1,2,3 依次进栈
栈的抽象数据类型
栈是线性表,但又有自身的特殊性,所以操作会有些变化
插入:改名为push 【压入“子弹”】
删除:改名为pop 【弹出“子弹”】
栈的顺序存储结构
以数组中下标为 0 的一端作栈底比较好,因为首元素都存在栈底,变化最小,所以让它做栈底
定义一个top变量指示栈顶元素在数组中的位置,则,栈顶位置top < 存储栈的长度StackSize
当栈存在一个元素时,top = 0
空栈的判定条件是top = -1
栈的结构定义:
假设一个栈,StackSize 是5:
进栈操作
代码:
出栈操作
进出栈操作没有涉及到任何循环语句,时间复杂度为O(1)
两栈共享空间
栈的缺陷:必须事先确定数组存储空间大小
对于一个栈,只能事先考虑周全
对于两个相同类型的栈,则可以最大程度利用已开辟的存储空间
栈1为空——> top1 = -1
栈2为空——> top2 = n-1+1 = n
栈满?
两栈共享空间的结构的代码:
两栈共享空间的push方法:【压入‘子弹’】
两栈共享空间的pop方法:【弹出‘子弹’】
两栈共享空间的前提:相同数据类型
两栈共享空间的最擅长情景:两个栈的空间需求有相反关系【即一增一减】
栈的链式存储结构及实现
栈只是栈顶来做插入、删除操作,把栈顶放在单链表的头部
对于链栈来说,是不需要头结点的
链栈基本不会出现栈满的情况【如有,内存已爆】
空链表——> 头指针指向空
栈链空栈——> top=NULL
链栈的结构代码:
链栈的操作大部分和单链表类似,只是插入、删除上特殊:
进栈
假设元素值为e的新结点是s,top为栈顶指针
出栈
假设变量p用来存储要删除的栈顶结点,将栈顶指针下移一位,最后释放p即可
进出栈的时间复杂度都是O(1)
如果栈的使用过程中元素变化不可预料,有时很小,有时很大,那么最好是利用链栈,反之,如果它的变化在可控范围内,建议使用顺序栈【因为存储时的定位很方便,但要事先确定一个固定的长度,可能会存在内存空间浪费的问题】
栈的作用
简化了程序设计的问题,使我们更加聚焦于要解决的问题核心
栈的应用——递归
斐波那契数列:
迭代:
递归:
递归:
把一个直接调用自身或通过一系列的调用语句间接调用自己的函数称为递归函数
但,不要陷入死循环,每个递归定义时至少有一个条件,满足条件时,递归不再进行【即不再引用自身而是返回值退出】
迭代——> 循环结构
递归——> 选择结构 ——> 清晰、简洁
#但是,大量的递归调用会建立函数的副本,会耗费大量的时间和内存
栈和递归的关系 【理解即可,用户不需要管理此栈,系统来做】
栈的应用——四则表达式求值
基本思想类似:遇到左括号( ——> 进栈——> 期间让数字运算——> 遇到右括号)——> 出栈
一种不需要括号的后缀表达法:后缀表示法
——>
计算机如何应用后缀表达式计算:
中缀表达式 转 后缀表达式
中缀表达式:
后缀表达式:
理解了上述,就理解了“栈”