【算法系列 二】Stack

青春壹個敷衍的年華 提交于 2019-12-07 21:49:12

栈应用的场景:
1.括号问题
2.后缀表达式
3.深度优先遍历
4.保存现场

1. 给定字符串,仅由“()[]{}”六个字符组成。设计算法,判断该字符串是否有效。

    括号必须以正确的顺序配对,如“()”、“()[]{}”是有效的,但"([)]"是无效的(Leetcode 20)。

思想就是碰到左括号压栈,右括号出栈,然后判断弹出的元素是不是一对,最后栈为空则是真的。

代码如下:

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

For example:

")()())":4

"()(()":2 

"()(())":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。



系列:

【算法系列 一】 Linked List

【算法系列 二】 Stack

【算法系列 三】 Quene

【算法系列 四】 String

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!