当前位置:   article > 正文

力扣 227.基本计算器 II_给你一个字符串表达式s,请你实现一个基本 加减乘除 java

给你一个字符串表达式s,请你实现一个基本 加减乘除 java

问题描述

给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

整数除法仅保留整数部分。

示例 1:

输入:s = "3+2*2"
输出:7
  • 1
  • 2

示例 2:

输入:s = " 3/2 "
输出:1
  • 1
  • 2

示例 3:

输入:s = " 3+5 / 2 "
输出:5
  • 1
  • 2

提示:

  1. 1 <= s.length <= 3 * 10^5
  2. s 由整数和算符 (’+’, ‘-’, ‘*’, ‘/’) 组成,中间由一些空格隔开
  3. s 表示一个 有效表达式
  4. 表达式中的所有整数都是非负整数,且在范围 [0, 231 - 1] 内
  5. 题目数据保证答案是一个 32-bit 整数

问题思路

思路1:双栈

​ 本题与224.基本计算器相比,加入了乘法运算和除法运算,由此出现了一个问题,如何维护运算符的优先级?为了能够复用224题的代码,这里我选择的策略是,先进行乘除法的计算,等遇到右括号或者扫描完毕后,统一计算加减法。

​ 同样,这里我们使用两个栈 number 和 symbol。分别用来存放操作数和操作符。

​ 对字符串进行一次扫描,规则如下:

​ 如果为空格,直接跳过。

​ 如果为‘+’、‘-’、‘(’、‘*’、‘/’,则直接入栈。

​ 如果为‘)’,则依次将符号栈的栈顶元素出栈,将数字栈中的操作数出栈,然后进行计算,计算后将结果存入数字栈,直到出栈符号为左括号时停止出栈。

​ 如果为数字,则首先继续扫描字符串,获得完整的数字。然后,根据符号栈栈顶元素进行不同的操作。如果是乘除法,则数字栈顶元素出栈,进行计算后将结果存入数字栈。如果为加号或左括号,则将操作数入栈。如果减号,则将操作数的相反数入栈。

​ 扫描结束后,符号栈内可能还有一些加号未运算,需要依次出栈并求和,得到最终结果。

​ 这里同样需要注意几个问题:

​ 第一,可能出现第一个数为负数,为了避免边界判断,我们在扫描前向数字栈入栈元素0。

​ 第二,左括号后的第一个数可能是负数,我们选择将 “(-” 替换为 “(0-”。

​ 实现代码如下,空间复杂度O(n),时间复杂度O(n)。运行时间258 ms。

class Solution {
	public int calculate(String s) {
		int n = s.length();
		char[] exp = s.toCharArray();
		Stack<Character> symbol = new Stack<Character>();
		Stack<Integer> number = new Stack<Integer>();
		// 防止第一个数为负数
		number.push(0);
		 // 将 (- 替换为 (0-
        s = s.replaceAll("\\(-", "(0-");
		int i = 0;
		while (i < n) {
			char c = exp[i++];
			if (c == ' ') {
				continue;
			} else if (c == '+' || c == '-' || c == '(' || c == '*' || c == '/') {
				symbol.push(c);
			} else {
				// 获取操作数
				int num;
				if (c == ')') {
					num = number.pop();
					char op = symbol.pop();
					while (op != '(') {
						int num2 = number.pop();
						System.out.println(num + " " + num2);
						if (op == '+') {
							num = num2 + num;
						}else if(op == '-'){
							num = num2 - num;
						}
						op = symbol.pop();
					};
				} else {
					num = c - '0';
					while (i < n && Character.isDigit(exp[i])) {
						num = 10 * num + exp[i++] - '0';
					}
				}
				// 若符号栈空, 则将操作数压栈
				System.out.println("num:" + num);
				if (symbol.isEmpty()) {
					number.push(num);
				} else {
					// 如果符号栈顶元素为运算符,则进行计算,并将结果压栈,如果为(,则直接将操作数压栈
					char top = symbol.peek();
					if (top == '*') {
						symbol.pop();
						number.push((number.pop() * num));
					} else if (top == '/') {
						symbol.pop();
						number.push((number.pop() / num));
					} else if (top == '-') {
						symbol.pop();
						symbol.push('+');
						number.push(-1 * num);
					} else {
						number.push(num);
					}
				}
			}
			System.out.println("number:" + number.toString());
			System.out.println("symbol:" + symbol.toString());
			System.out.println();
		}
		// 对栈内元素累加
		while (!symbol.isEmpty()) {
			symbol.pop();
			number.push(number.pop() + number.pop());
		}
		return number.pop();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

总结

​ 此题为了224题的代码能够复用,其实是采取了一个投机取巧的办法。事实上,计算应该发生在读取到符号时,而不是读取到数字时。

​ 选择在读取数字时计算,思路就是要根据该数字前的最后一个操作符计算出结果,然后将结果计算出并压栈,但是在有多个优先级的运算符时,往往需要考虑该数字后的第一个操作符的优先级问题,就会比较复杂,难以判断。

​ 一个好的思路就是在读取到符号时进行计算。首先定义一个字典类型,表示不同运算符的优先级。如果读取到运算符op,则将op之前直到左括号的所有优先级大于等于op的运算符出栈并计算;如果读取到右括号,则将直到左括号的所有运算符依次出栈并计算。这样做的好处就是保证了在同一括号内,运算符的优先级始终非严格单调递增,从而保证了结果的正确性。

​ 另外,这样做还可以及时的处理栈内与op优先级相同的运算,防止不满足交换律的运算出错,例如 3 - 2 + 3,如果到最后一起算的话,由于栈的先入后出的特性,会先计算2+3=5,在计算3-5=-2。很明显结果不正确。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/848164
推荐阅读
相关标签
  

闽ICP备14008679号