栈应用的场景:
1.括号问题
2.后缀表达式
3.深度优先遍历
4.保存现场 java
1. 给定字符串,仅由“()[]{}”六个字符组成。设计算法,判断该字符串是否有效。 算法
括号必须以正确的顺序配对,如“()”、“()[]{}”是有效的,但"([)]"是无效的(Leetcode 20)。 lua
思想就是碰到左括号压栈,右括号出栈,而后判断弹出的元素是否是一对,最后栈为空则是真的。 spa
代码以下: .net
public class Solution { public boolean isValid(String s) { Stack<Character> stack = new Stack<Character>(); boolean isvalid = true; char[] c = s.toCharArray(); char temp; for (int i = 0; i < c.length; i++) { switch (c[i]) { case '(': stack.push(c[i]); break; case ')': if(stack.isEmpty()) { isvalid = false; }else { temp = stack.pop(); if (temp != '(') { isvalid = false; } } break; case '[': stack.push(c[i]); break; case ']': if(stack.isEmpty()) { isvalid = false; }else { temp = stack.pop(); if (temp != '[') { isvalid = false; } } break; case '{': stack.push(c[i]); break; case '}': if(stack.isEmpty()) { isvalid = false; }else { temp = stack.pop(); if (temp != '{') { isvalid = false; } } break; default: break; } } if(isvalid) { if(!stack.isEmpty()) { isvalid = false; } } return isvalid; } }
顺便提一下:关于括号匹配问题,还有一个括号的组成个数问题,给定n组括号,问有多少种合法表达式,也是应用栈。(参kao这里,算法篇第8题) 设计
2. 给定字符串,仅包含"("和")",它可能不是全匹配的,设计算法找出最长匹配的括号子串,返回该子串的长度。(Leetcode 32) code
For example: blog
")()())":4 token
"()(()":2 leetcode
"()(())":6
看到括号问题,第一反应就是用栈,一个简单的思想就是与第一问同样,每次匹配成功,则把成功的位置作个记号,最后看记号的最长子串是多少便可好比"()(()"记号就能够是"YYNYY"。要遍历两次,时间复杂度为O(2n) = O(n)
public class Solution { public int longestValidParentheses(String s) { char[] c = s.toCharArray(); boolean[] b = new boolean[c.length]; Stack<Integer> stack = new Stack<Integer>(); for (int i = 0; i < c.length; i++) { if (c[i] == '(') { stack.push(i); } else if (c[i] == ')' && !stack.isEmpty()) { int index = stack.pop(); b[index] = true; b[i] = true; } } int maxLength = 0; int length = 0; for (int i = 0; i < c.length; i++) { if (b[i]) { length++; } else { length = 0; } maxLength = maxLength > length ? maxLength : length; } return maxLength; } }
以上的方法须要遍历两次,有没有只遍历一次的方法呢?
咱们定义两个变量,start=-1,最大匹配长度ml=0
变量i为当前扫描的字符
栈内只多是"(",碰到"("就压栈,碰到")"就出栈。因为咱们要计算的最长匹配长度,因此压栈的是"("的下标方便计算长度。
在任什么时候候,只要栈不空,那么存在栈里的元素的意义就是当前还没有被匹配的‘(’。由于若匹配,咱们会进行pop操做,因此在进行一次匹配后,咱们若判断栈不空,那就说明至少栈顶的元素是没有匹配的,而这个没有匹配的栈顶‘(’,极有可能到最后也得不到匹配,因此咱们这时要及时的更新最大长度,那么当前的已经匹配的长度是多少呢?是当前访问元素的index - 栈顶元素的值。若进行一次匹配操做后,发现栈为空了,这说明什么呢?说明是处于连续的匹配中,这时咱们也要跟新最大长度,由于极有可能在之后就不匹配了,当前这个匹配的最大结果要进行保留。可是此时,咱们只有当前遍历元素的下标,另一个值是什么呢?也就是减数是多少呢?因此咱们还须要定义一个变量start,用来表示每一次连续匹配的起始点,咱们用当前的下标- start来表示当前的最大长度。那么若当前遍历的元素为‘)’,且此时栈为空,说明什么呢?说明在此以前,就没有多余的‘(’,说明这就是个坑,是个不匹配的点,此时咱们要作些什么呢?简单的将该字符跳过,去处理下一个字符吗?回头看看咱们刚才分析中,涉及到了一个连续匹配的起始点问题,对,咱们要更改这个连续匹配的起始点,令其等于当前处理的‘)’字符的位置。
因此整体流程以下:
若是为"(",压栈
若是为")":
栈为空:start = i
栈不为空:出栈
此时栈为空:ml = i -start
栈不为空:ml = i - 栈顶元素
代码:
public class Solution { public int longestValidParentheses(String s) { char[] c = s.toCharArray(); int start = -1; int ml = 0; Stack<Integer> stack = new Stack<Integer>(); for (int i = 0; i < c.length; i++) { if (c[i] == '(') { stack.push(i); } else { if (stack.isEmpty()) { start = i; } else { stack.pop(); if (stack.isEmpty()) { ml = (i - start) > ml ? (i - start) : ml; } else { ml = (i - stack.peek()) > ml ? (i - stack.peek()) : ml; } } } } return ml; } }
时间复杂度O(n)
3. 计算给定的后缀表达式值,有效操做只有+-*/,每一个操做数都是整数。(Leetcode 150)
Some examples:
["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9 ["4", "13", "5", "/", "+"] -> (4 + (13 / 5)) -> 6除了括号问题,后缀表达式也是一个经常使用栈来处理的问题。碰到数字就压栈,碰到操做符弹出两个数字计算后,结果再压栈。
代码:
public class Solution { public int evalRPN(String[] tokens) { Stack<Integer> stack = new Stack<Integer>(); int res = 0; int a,b; for (int i = 0; i < tokens.length; i++) { switch (tokens[i]) { case "+": a = stack.pop(); b = stack.pop(); res = a + b; stack.push(res); break; case "-": a = stack.pop(); b = stack.pop(); res = b - a; stack.push(res); break; case "*": a = stack.pop(); b = stack.pop(); res = b * a; stack.push(res); break; case "/": a = stack.pop(); b = stack.pop(); res = b / a; stack.push(res); break; default: stack.push(Integer.parseInt(tokens[i])); res = Integer.parseInt(tokens[i]); break; } } return res; } }
注意相似["18"]这种状况,须要返回18。
系列: