启示:
-
栈与队列
栈是限定仅在表尾进行插入和删除操做的线性表
队列是只容许在一端进行插入操做,而在另外一端进行删除操做的线性表
目录
<!-- GFM-TOC -->html
<!-- GFM-TOC -->java
========================================git
4.1 开场白
- 一些能够略过的场面话...
- 以左轮手枪和弹夹式手枪的区别引入
========================================github
4.2 栈的定义
-
栈的引入:
-
栈的常见应用:
软件中的撤销(undo)操做,浏览器历史纪录,Android中的最近任务,Activity的启动模式,CPU中栈的实现,Word自动保存,解析计算式,解析xml/json
-
定义:栈(stack)是限定仅在表尾进行插入和删除操做的线性表
-
栈首先是一个线性表,栈元素具备线性关系,即前驱后继关系。
-
栈顶(top):容许插入和删除的一端称为栈顶
-
栈底(bottom):另外一端称为栈底
-
定义所述“表尾插入删除”,表尾指的是栈顶,而不是栈底
-
特殊的线性表:特殊在于限制了插入删除的位置,始终在栈顶进行,栈底是固定的
-
空栈:不含任何数据元素的栈称为空栈
-
LIFO:last in first out,栈又称为后进先出的线性表,简称LIFO结构
-
栈的插入操做:进栈,也称压栈、入栈
-
栈的删除操做:出栈,也称弹栈
-
进栈出栈变化形式
-
举例来讲,三个int型数字元素1,2,3依次进栈,出栈次序有5种
- 321:123进,321出
- 123:1进1出,2进2出,3进3出
- 213:12进,21出,3进3出
- 132:1进1出,23进,32出
- 231:12进,2出,3进,31出
-
求出栈序列个数:卡特兰数公式:C(2n,n)/(n+1)
- ### 其中,卡特兰数前几项为: 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796json
-
栈的应用之n阶汉诺塔盘问题(Hanoi)
-
规则:
-
在游戏中,总共有n个金属盘片的塔叫作n阶汉诺塔

(没搞懂原理,也没搞懂代码,可是先把结论记下来)c#
========================================数组
4.3 栈的抽象数据类型
-
栈做为线性表,理论上线性表的操做特性都具有
-
针对特殊性,插入和删除更名为push和pop,即压和弹

========================================浏览器
4.4 栈的顺序存储结构及实现
-
一、栈的顺序存储结构
-
栈的顺序存储也是线性表顺序存储的简化,简称为顺序栈
-
数组中,用下标为0的一端做为栈底,由于首元素都存在栈底,变化小
-
栈顶top:若存储栈长度为StackSize,则top必须小于StackSize
-
空栈:栈中有一个元素时top=0,以示区别,空栈的断定条件设为top=-1
-
栈的结构定义:





-
时间复杂度:
-
进栈、出栈不涉及任何循环语句,时间复杂度均为O(1)
========================================数据结构
4.5 两栈共享空间
-
两栈共享空间的关键思路:
-
两个栈底分别位于数组的始端(下标0)和数组的末端(下标数组长度n-1),增长元素即从两端点向中间延伸
-
两栈共享空间的前提:
-
两栈共享空间的图示:

-
栈满:
-
通常状况:两个栈见面之时,即两个指针相差1,top1+1==top2时
-
极端状况:2空时,top1=n-1,则1满;1空,top2=0,则2满
-
两栈共享空间的结构代码:

-
两栈共享空间的push方法代码实现:
除了要插入元素值参数,还须要一个判断是栈1仍是2的栈号参数stackNumber

-
两栈共享空间的pop方法代码实现:
参数仅为判断栈1或2的参数stackNumber

========================================函数
4.6 栈的链式存储结构及实现
-
栈的链式存储结构
-
栈的链式存储结构,简称链栈
-
栈顶放在单链表的头部,且不须要头结点
-
空栈:链表原定义为头指针指向空,故链栈的空是top=NULL
-
图示:


-
链栈的操做大部分与单链表相似,只是插入、删除特殊
-
栈的链式存储结构--进栈操做




-
时间复杂度:
-
链栈的push和pop均不涉及循环操做,时间复杂度为O(1)
========================================
4.7 栈的做用
-
类比明明能够靠脚走到世界上任何地方,为何还要乘坐飞机火车呢
-
栈的引入简化了程序涉及的问题,划分了不一样关注层次,使思考范围缩小,更加聚焦于问题核心
(好比像数组之类的,要分散精力去考虑数组的下标增减等细节问题。。)
-
如今许多高级语言如java,c#等,都有对栈结构的封装,不用关心实现细节,直接使用Stack的push和pop方法,很是方便
========================================
4.8 栈的应用--递归



-
递归定义
-
把一个直接调用本身或者经过一系列的调用语句间接地调用本身的函数,称为递归函数。
-
每一个递归定义必须至少有一个条件,知足时递归再也不进行,即再也不引用自身而是返回值退出。
-
迭代和递归的区别:
- 迭代使用循环结构,递归使用选择结构
- 递归使程序的结构更清晰、更简洁
- 递归调用会创建函数的副本,会耗费大量的时间和内存
-
递归与栈结构:
- 递归过程退回的顺序是它前进顺序的逆序。在退回过程当中,可能要执行某些动做,包括恢复在前进过程当中存储起来的某些数据。
- 这种存储某些数据,并在后面又以存储的逆序恢复这些数据,以提供以后使用的需求,这显然很符合栈这样的数据结构。
========================================
4.9 栈的应用--四则运算表达式求值
-
后缀(逆波兰)表示法定义
-
一种不须要括号的后缀表达法,也称为逆波兰(Reverse Polish Notation, RPN)
-
后缀: 全部的符号都是要在运算数字的后面出现的。
-
没了括号,机器能够更好地执行。
-
中缀表达式
-
定义:经常使用的标准四则运算表达式,全部的运算符号都在两数字的中间
-
想要让计算机处理常见的中缀表达式,最重要的有两步:
-
一、将中缀表达式转化为后缀表达式(栈用来进出运算的符号)
-
二、将后缀表达式进行运算得出结果(栈用来进出运算的数字)
-
中缀表达式转后缀表达式(数字所有输出,只有符号进栈出栈)
-
规则:
-
从左到右遍历中缀表达式的每一个数字和符号
-
如果数字就输出,即成为后缀表达式的一部分
-
如果符号,先判断其与栈顶符号的优先级
-
是右括号或优先级低于栈顶符号,则栈顶元素依次出栈并输出,并将当前符号进栈
-
若优先级高于栈顶符号,则不用管栈顶,直接压栈
-
一直到最终输出后缀表达式为止
-
图示:
- ### 注:右括号不进栈,当匹配到左括号时,直接到左括号的部分出栈
-
后缀表达式计算结果(只有数字进栈出栈)
-
规则:
-
从左到右遍历表达式的每一个数字和符号
-
遇到数字就进栈
-
遇到符号,就将处于栈顶的两个数字出栈,并运算,且运算结果进栈
-
一直到最终得到结果
-
图示:
- ### 注:遇运算符取出两数计算,而后须要结果再次进栈,最终结果会出如今栈里
-
示例:
-
中缀表达式:9 + ( 3 - 1 ) * 3 +10 / 2
-
后缀表达式:9 3 1 - 3 * + 10 2 / +
========================================
4.10 队列的定义
-
队列的引入:
-
常见应用:电脑卡壳时,过一下子又自顾自地按顺序把以前的操做执行一遍;客服排队;键盘输入到显示器输出等,都是队列
-
定义:队列(queue)-- 只容许在一端进行插入操做,而在另外一端进行删除操做的线性表
-
容许插入的一端称为队尾,容许删除的一端称为队头
(先进先出,所以头做为先进的,会先出,所以删除端是队头)
-
FIFO:first in first out,先进先出的线性表
-
图示:设q = (a1, a2, ... , an)

========================================
4.11 队列的抽象数据类型
-
队列一样是线性表,也有相似线性表的各类操做
-
不一样在于:插入只能在队尾,删除只能在队头

========================================
4.12 循环队列
-
注:栈和队列做为特殊的线性表,一样都具备顺序存储和链式存储两种存储方式。
-
队列顺序存储的不足
-
当队头固定设为数组下标为0的位置时:入队O(1),但出队O(n)

-
当不限制队头必须在数组下标为0的位置时,能够提升一些性能

-
引入两个指针:front指向队头,rear指向队尾的下一个位置
当空队列:front=rear


-
循环队列定义
-
定义:队列的头尾相接的顺序存储结构称为循环队列。
-
从新定义队列的空和满
-
QueueSize:队列的最大尺寸
-
队列空: front == rear
-
队列满: front == (rear + 1) % QueueSize
(修改队列满的条件,保留一个元素空间)

-
计算队列长度的公式:(rear - front + QueueSize) % QueueSize
-
循环队列的代码实现





-
循环队列的相关条件和公式:
-
1.队空条件:rear==front
-
2.队满条件:(rear+1) %QueueSIze==front,其中QueueSize为循环队列的最大长度
-
3.计算队列长度:(rear-front+QueueSize)%QueueSize
-
4.入队:rear = (rear+1)%QueueSize
-
5.出队:front = (front+1)%QueueSize
========================================
4.13 队列的链式存储结构及实现
-
定义:
-
队列的链式存储结构,其实就是线性表的单链表,但只能尾进头出,简称链队列。
-
图示:





-
队列的链式存储结构--出队
-
出队,即头结点的后继结点出队,将头结点的后继改成它后面的结点
-
若链表除头结点外,只剩一个元素,则需将rear指向头结点
-
图示:


-
时间空间复杂度分析 -- 循环队列和链队列对比
-
时间上:都是O(1)
-
空间上:
-
循环队列:事先申请好空间,使用期间不释放
-
链队列:每次申请和释放结点也会存在一些时间开销
-
循环队列:固定长度,故存在存储元素个数和空间浪费的问题
-
链队列:须要指针域,产生一些空间上的开销,在空间上更灵活
========================================
4.14 总结回顾

========================================
4.15 结尾语
END