一、栈的基础知识
栈是⼀种“先进后出”(FILO, First In Last Out)的数据结构。
二、经典面试题
1.栈的基本操作
利⽤两个栈来实现,⼀个输⼊栈、⼀个输出栈。
输⼊栈⽤于读⼊数据。当需要输出元素时,若输出栈为空,则将输⼊栈的所有元素 推送到输出栈,然后取栈顶元素;若输出栈⾮空,则输出栈顶即可。
输出栈的作⽤是对已经被反转的序列进⾏⼆次反转。对此感到困惑的同学可以画图模拟⼀下。
按照题意实现即可。
被出栈的元素只有两种可能:即将⼊栈的元素 和 当前栈顶的元素。
只需要关注出栈序列,分类讨论后模拟即可。
2.栈结构扩展应用
LeetCode #20 有效的括号
结论1:在任意⼀个位置上,左括号数量>=右括号数量
结论2:在最后⼀个位置上,左括号数量==右括号数量
根据上述两个结论,程序中只需要记录左括号和右括号的数量即可。
⼀对括号可以等价为⼀个完整的事件。左括号可以看作事件的开始、右括号可以看
作事件的结束。⽽括号序列可以看作事件与事件之间的完全包含关系。
栈可以处理具有完全包含关系的问题。
LeetCode #1021 删除最外层的括号
左括号和右括号差值为0时,代表这⼀串括号序列是独⽴的,可以被单独分解出来。
LeetCode #1249 移除⽆效的括号
可以被匹配的括号都是有效的,⽽其他的括号都需要被删除。
LeetCode #145 ⼆叉树的后序遍历
递归做法⽐较简单,在这⾥介绍⼀下基于迭代的做法。
技巧是使⽤两个栈,⼀个数据栈,⼀个状态栈。将“遍历左⼦树”,“遍历右⼦树”和“访问根节点”三个步骤分别⽤状态码表⽰,枚举状态转移过程,使⽤有限状态⾃动机(FSM, Finite State Machine)的模型来模拟递归过程。
LeetCode #331 验证⼆叉树的前序序列化
思路1:每次拆掉⼀个“数字、#、#”的节点(即叶⼦结点),最后树上的全部节点都会被拆光(即只剩⼀个“#”),能拆光的序列就是合法序列。
思路2:初始状态有⼀个坑。每多增加⼀个数字节点,会在占掉⼀个坑后,产⽣两个坑,每多增加⼀个#,会减少⼀个坑。合法的⼆叉树前序遍历最后会刚好⽤完所有的坑。
LeetCode #227 基本计算器II
思路1:找到式⼦中优先级最低的运算符,然后递归分治运算两侧的⼦式即可。
思路2:使⽤操作数栈和操作符栈辅助计算,当操作符栈遇到更低优先级的操作符时,需要将之前更⾼级别的操作符对应的结果计算出来。
对于有括号的情况,左括号相当于提⾼了内部全部运算符的优先级,当遇到右括号的时候需要将匹配的括号间的内容全部计算出来。
可以通过加⼀个特殊操作符的处理技巧,来额外处理结尾的数字。
智力发散题
LeetCode #636 函数的独占时间
本质就是⼀道模拟题,画⼀个线段图能显著地辅助理解。任务开始时进栈,上⼀个任务暂停执⾏;任务完成时出栈,恢复上⼀个任务的执⾏。