LeetCode 224. 基本计算器

224. 基本计算器

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

注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。

 

示例 1:

输入:s = "1 + 1"
输出:2

示例 2:

输入:s = " 2-1 + 2 "
输出:3

示例 3:

输入:s = "(1+(4+5+2)-3)+(6+8)"
输出:23

提示:

  • 1 <= s.length <= 3 * 10^5
  • s 由数字、'+''-''('')'、和 ' ' 组成
  • s 表示一个有效的表达式
  • '+' 不能用作一元运算(例如, "+1" 和 "+(2 + 3)" 无效)
  • '-' 可以用作一元运算(即 "-1" 和 "-(2 + 3)" 是有效的)
  • 输入中不存在两个连续的操作符
  • 每个数字和运行的计算将适合于一个有符号的 32位 整数

解法1:双栈

同类题型:LeetCode 227. 基本计算器 II-CSDN博客        LeetCode 772. 基本计算器 III-CSDN博客

算法逻辑

  1. 栈的使用:我们使用两个栈,opStack 用于存放操作符(包括括号),numStack 用于存放数字。栈用于处理操作符的优先级和括号内的计算。
  2. 字符串预处理:去除字符串中的所有空格,以简化后续的字符读取。
  3. 遍历字符串:逐个字符读取字符串,根据当前字符的类型(数字或操作符),执行不同的操作。
  4. 处理数字:当遇到数字时,连续读取形成完整的数字,并将其压入数字栈 numStack
  5. 处理操作符:当遇到操作符时,根据操作符的类型(加号或减号),从操作符栈 opStack 中弹出操作符,并从数字栈 numStack 中弹出相应的数字,执行计算,然后将结果压回数字栈。
  6. 处理括号:遇到左括号时,压入操作符栈;遇到右括号时,进行计算,直到遇到左括号,同时弹出左括号。

算法实现步骤

  1. 初始化两个栈:操作符栈 opStack 和数字栈 numStack
  2. 遍历字符串 s,对于每个字符 c
    • 如果 c 是左括号 '(',则压入操作符栈。
    • 如果 c 是右括号 ')',则进行计算,直到遇到左括号,并弹出左括号。
    • 如果 c 是数字,则连续读取形成完整数字,并压入数字栈。
    • 如果 c 是操作符(加号或减号):
      • 如果当前位置为字符串开头或前一个字符是左括号,压入数字栈一个初始数字 0。
      • 进行必要的计算,将操作符和数字从栈中弹出并执行运算,结果压回数字栈。
      • 将当前操作符压入操作符栈。
  3. 完成字符串遍历后,如果操作符栈中仍有操作符,继续进行计算,直到操作符栈为空。
  4. 最终,数字栈的栈顶元素即为表达式的计算结果。

Java版:

class Solution {
    public int calculate(String s) {
        // 创建两个栈,分别用于存放数字和操作符
        Deque<Character> opStack = new ArrayDeque<>();
        Deque<Integer> numStack = new ArrayDeque<>();

        // 去掉字符串中的所有空格,方便后续处理
        s = s.replaceAll(" ", "");

        int i = 0;
        int n = s.length();
        while (i < s.length()) {
            char c = s.charAt(i);
            // 如果当前字符是左括号,压入操作符栈
            if (c == '(') {
                opStack.push(c);
            } else if (c == ')') {
                // 如果当前字符是右括号,进行计算直到遇到左括号
                while (!opStack.isEmpty() && opStack.peek() != '(') {
                    char op = opStack.pop();
                    int num2 = numStack.pop();
                    int num1 = numStack.pop();
                    if (op == '+') {
                        numStack.push(num1 + num2);
                    } else {
                        numStack.push(num1 - num2);
                    }
                }
                // 弹出左括号
                opStack.pop();
            } else if (Character.isDigit(c)) {
                // 如果当前字符是数字,连续读取形成完整数字并压入数字栈
                int num = 0;
                while (i < n && Character.isDigit(s.charAt(i))) {
                    num = num * 10 + (s.charAt(i) - '0');
                    i++;
                }
                i--; // 此时的i是第一个不为数字的位置,将i调整到i-1
                numStack.push(num);
            } else {
                // ch == '+' || ch == '-' 当前字符是操作符
                // 将初始数字0压入数字栈,作为累加的基准,防止第一个数为负数,例如 -1
                if (i == 0 || s.charAt(i - 1) == '(') {
                    numStack.push(0);
                }
                // 先进行必要的计算,然后将操作符压入操作符栈
                while (!opStack.isEmpty() && opStack.peek() != '(') {
                    char op = opStack.pop();
                    int num2 = numStack.pop();
                    int num1 = numStack.pop();
                    if (op == '+') {
                        numStack.push(num1 + num2);
                    } else {
                        numStack.push(num1 - num2);
                    }
                }
                opStack.push(c);
            }
            i++;
        }
        // 处理剩余的操作符
        while (!opStack.isEmpty() && opStack.peek() != '(') {
            char op = opStack.pop();
            int num2 = numStack.pop();
            int num1 = numStack.pop();
            if (op == '+') {
                numStack.push(num1 + num2);
            } else {
                numStack.push(num1 - num2);
            }
        }
        return numStack.peek();
    }
}

Python3版:

class Solution:
    def calculate(self, s: str) -> int:
        # 创建两个栈,分别用于存放数字和操作符
        opStack = []
        numStack = []
        # 去掉字符串中的所有空格,方便后续处理
        s = s.replace(" ", "")
        i = 0 
        n = len(s)
        while i < n:
            # 如果当前字符是左括号,压入操作符栈
            if s[i] == '(':
                opStack.append(s[i])
            elif s[i] == ')':
                # 如果当前字符是右括号,进行计算直到遇到左括号
                while opStack and opStack[-1] != '(':
                    op = opStack.pop()
                    num2 = numStack.pop()
                    num1 = numStack.pop()
                    if op == '+':
                        numStack.append(num1 + num2)
                    else:
                        numStack.append(num1 - num2)
                # 弹出左括号
                opStack.pop()
            elif s[i].isdigit():
                # 如果当前字符是数字,连续读取形成完整数字并压入数字栈
                num = 0
                while i < n and s[i].isdigit():
                    num = num * 10 + int(s[i])
                    i += 1
                i -= 1
                numStack.append(num)
            else:
                # 如果当前字符是操作符,先进行必要的计算,然后将操作符压入操作符栈
                # 将初始数字0压入数字栈,作为累加的基准,防止第一个数为负数,例如 -1
                if i == 0 or s[i - 1] == '(':
                    numStack.append(0) 

                while opStack and opStack[-1] != '(':
                    op = opStack.pop()
                    num2 = numStack.pop()
                    num1 = numStack.pop()
                    if op == '+':
                        numStack.append(num1 + num2)
                    else:
                        numStack.append(num1 - num2)

                opStack.append(s[i])  

            i += 1
        
        # 处理剩余的操作符
        while opStack and opStack[-1] != '(':
            op = opStack.pop()
            num2 = numStack.pop()
            num1 = numStack.pop()
            if op == '+':
                numStack.append(num1 + num2)
            else:
                numStack.append(num1 - num2)

        return numStack[-1]

复杂度分析

  • 时间复杂度:O(n),其中 n 是字符串 s 的长度。这是因为每个字符最多被遍历两次(一次在循环中,一次可能在处理右括号时),所以总体操作次数与字符串长度成正比。
  • 空间复杂度:O(n),最坏情况下,所有字符都可能是数字或操作符,需要存储在栈中,因此所需的栈空间与字符串长度成正比。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值