1.栈
2.栈的典型应用场合
1)逆序输出:输出次序与处理过程颠倒,递归深度和输出长度不易预知
2)递归嵌套:具有自相似性的问题可递归描述,但分支位置和嵌套深度不固定
3)延迟缓冲:
4)栈式计算:RPN,基于栈结构的特定计算模式
3.进制转换
4.括号匹配
思路:消去一对紧邻的左右括号,不影响全局的匹配判断
问题在于如号找到这对括号?如何使问题的这种简化得以持续进行?
顺序扫描表达式,用栈记录已扫描的部分
反复迭代:凡遇"(",则进栈;凡遇")",则出栈,若最后一个)处理完后,整个栈变空,原来的表达式为匹配的
使用栈结构
import java.util.Stack; public class StackDemo{ public static void main(String [] args){ String str="(1+2+((22+2)*98)+(32-2))*22"; StringBuilder sb=new StringBuilder(str); Stack<Character> stack=new Stack<Character>(); int len=sb.length(); for(int i=0;i<len;i++){ char c=sb.charAt(i); if(c!='('&&c!=')') continue; if(c=='('){ stack.push(c); }else if(c==')'&&!stack.empty()){ stack.pop(); }else{ System.out.println("not match"); } } System.out.println(stack.empty()); } }
使用计数器进行判定是否匹配
以0开始,以0结束
采用栈结构,可以便捷地推广至多种括号并存的情况。
5.栈混洗
A---->S---->B
栈混洗计数
catalan(n)=(2n)!/(n+1)!n!
任意三个元素能否按某相对次序出现于混洗中,与其他元素无关
甄别栈混洗:
对于任何1<=i<j<k<=n,[...,k,...,i,...,j...]必非栈混洗
一个排列如果不包含上述序列,则一定是栈混洗
O(n)算法,直接借助栈A,B和S,模拟混洗过程,每次S.pop()之前,检测S是否已空;或需弹出的元素在S中,却非顶元素
每一栈混洗,都对应于栈S的n次push与n次pop操作构成的序列
( ( ( ) ) ( ) ) push(1) push(2) push(3) pop(3) pop(2) push(4) pop(4) pop(1)
6.中缀表达式求值
计算次序未必与扫描的次序一致,将所有扫描过的部分保存到栈中
求值算法=栈+线性扫描
使用两个栈,分别存放运算符和数字
当数字栈中遇到连续数字时,应将当前数字栈顶的数乘以10加上当前数字再入栈。
“(”后遇到运算符时,大部分都执行运算符入账操作,“)”后遇到运算符时,大部分都执行运算符出栈操作。
7.逆波兰表达式,RPN
在由运算符和操作数组成的表达式中不使用括号,即可表示带优先级的运算关系
0 ! 1+ 2 3 ! 4 - 5 ^ - 67 * - 8 - 9 +
只需一个栈即可,每遇到一个数字则入栈,每遇到一个操作符则执行计算
8.中缀表达式到RPN表达式的转换
(0!+1)^(2*3!+4-5) {([0!]+1)^([(2*[3!])+4]-5)} {([ 0 ] ! 1 ) + ( [ ( 2 [3]! ) * 4 ] + 5) - }^ 0 ! 1 + 2 3 ! * 4 + 5 - ^
变换之后,数字的相对次序并没有改变
转换算法:
若读取的为数字则直接入RPN栈,如果读取的为运算符,只有当运算符栈顶的元素优先级高于该运算符时,将栈顶的运算符出栈,压入RPN栈