快手 CodeTop LeetCode 227.基本计算器Ⅱ

思路:利用栈实现表达式求值。

1.对于表达式求值的问题,可以维护两个栈,一个nums栈用来记录数字,一个ops栈用来记录操作符,遍历表达式,当遇到操作符时就进行数的相应计算。

2.首先可以定义一个eval()函数,用于从数字栈nums中弹出两个数字a和b,再从操作符栈ops中弹出操作符号,进行计算后将结果数字加入到数字栈nums中。

3.然后从前往后扫描整个表达式:

(1)当遇到空格' '时,跳过。

(2)当遇到数字时,读取一个连续的完整数字,再将其加入到数字栈nums中。

(3)当遇到'+'、'-'、'*'、'/'运算符时,需要考虑优先级,可以用HashMap保存每种操作符的优先级。

(a)如果操作符栈ops的栈顶元素的优先级比当前遇到的操作符的优先级高或者相等,则while()循环进行eval()操作,即将数字栈nums栈顶的两个元素取出来,然后和操作符栈ops的栈顶操作符进行相应的计算,并将计算结果压回数字栈nums中(由于栈的先进后出的特点,后进来的优先级高的操作符会先被用于计算)。

(b)否则,将当前运算符压入操作符栈ops中。

(4)扫描完整个表达式后,如果操作符栈ops中还存在元素,则while循环进行eval()操作。

(5)最终数字栈nums剩下的那个元素就是最终的结果,return返回。

4.细节处理:

(1)由于运算符有优先级,所以设计一个哈希表来存储' + '' - '' * '' / '的优先级,我们将+和-设计为1优先级,将*和/设计为2优先级。

(2)考虑到表达式s的第一个数字可能为负数,因此我们给s开头添加一个字符0。

5.复杂度分析:

(1)时间复杂度:O(n),其中n是字符串s的长度。

(2)空间复杂度:O(n)。

附代码:

java 复制代码
class Solution {
    private LinkedList<Integer> nums = new LinkedList<>();
    private LinkedList<Character> ops = new LinkedList<>();
    private Map<Character,Integer> map = new HashMap<>();
    public int calculate(String s) {
        s = '0' + s; // 对开头是负数的处理
        map.put('+', 1);   //定义运算符的优先级
        map.put('-', 1);
        map.put('*', 2);
        map.put('/', 2);
        for(int i = 0; i < s.length();i ++)
        {
            char c = s.charAt(i);
            // 因为乘除法需要立即计算,所以采用的是增强for循环,而不是普通的for i索引for循环
            // 逐i位遍历(而非增强for循环的话),每一位都要判断空格(上一题增强for循环无需判断)
            if(c == ' ') continue;  // 跳过空格
            if(c >= '0' && c <= '9')  // c是数字,读取一个连续的数字
            {
                int x = 0;
                while(i < s.length() && s.charAt(i) >= '0' && s.charAt(i) <= '9'){
                    x = x * 10 + s.charAt(i++) - '0';
                }
                // 抵消循环内最后一次i++造成的额外增量
                i--;
                nums.push(x);
            }
            else  // c是操作符
            {     // ops栈非空并且栈顶操作符优先级大于等于当前操作符c的优先级,进行eval()计算
                while(!ops.isEmpty() && map.get(ops.peek()) >= map.get(c)){
                    // 栈里元素进行计算
                    eval();
                }
                // 再把当前操作符c入栈
                ops.push(c);
            }
        }
        while(!ops.isEmpty()){
            // 栈里元素进行计算
            eval();
        }
        // 所有的计算完毕,数字栈中只剩下了最后一个最终计算结果,return即可
        return nums.pop();
    }
    public void eval()
    {
        int b = nums.pop();
        int a = nums.pop();
        char c = ops.pop();
        int res = 0;
        if(c == '+') res = a + b;
        else if(c == '-') res = a - b;
        else if(c == '*') res = a * b;
        else res = a / b;
        // 结果入栈
        nums.push(res);
    }
}

ACM模式:

java 复制代码
import java.util.LinkedList;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

class Solution {
    private LinkedList<Integer> nums = new LinkedList<>();
    private LinkedList<Character> ops = new LinkedList<>();
    private Map<Character, Integer> map = new HashMap<>();

    public int calculate(String s) {
        // 清空之前的数据(防止多次调用)
        nums.clear();
        ops.clear();
        map.clear();

        s = '0' + s; // 对开头是负数的处理
        map.put('+', 1);   // 定义运算符的优先级
        map.put('-', 1);
        map.put('*', 2);
        map.put('/', 2);

        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            // 因为乘除法需要立即计算,所以采用的是增强for循环,而不是普通的for i索引for循环
            // 逐i位遍历(而非增强for循环的话),每一位都要判断空格(上一题增强for循环无需判断)
            if (c == ' ') continue;  // 跳过空格

            if (c >= '0' && c <= '9') {  // c是数字,读取一个连续的数字
                int x = 0;
                while (i < s.length() && s.charAt(i) >= '0' && s.charAt(i) <= '9') {
                    x = x * 10 + (s.charAt(i) - '0');
                    i++;
                }
                // 抵消循环内最后一次i++造成的额外增量
                i--;
                nums.push(x);
            } else {  // c是操作符
                // ops栈非空并且栈顶操作符优先级大于等于当前操作符c的优先级,进行eval()计算
                while (!ops.isEmpty() && map.get(ops.peek()) >= map.get(c)) {
                    // 栈里元素进行计算
                    eval();
                }
                // 再把当前操作符c入栈
                ops.push(c);
            }
        }

        while (!ops.isEmpty()) {
            // 栈里元素进行计算
            eval();
        }

        // 所有的计算完毕,数字栈中只剩下了最后一个最终计算结果,return即可
        return nums.pop();
    }

    private void eval() {
        int b = nums.pop();
        int a = nums.pop();
        char c = ops.pop();
        int res = 0;
        if (c == '+') res = a + b;
        else if (c == '-') res = a - b;
        else if (c == '*') res = a * b;
        else res = a / b;
        // 结果入栈
        nums.push(res);
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 读取表达式字符串(可能包含空格)
        String s = scanner.nextLine();

        // 计算结果
        Solution solution = new Solution();
        int result = solution.calculate(s);

        // 输出结果
        System.out.println(result);

        scanner.close();
    }
}
相关推荐
小bo波7 小时前
使用Thread子类创建线程 VS 使用Runnable接口创建线程的区别
java·多线程·thread·并发编程·runnable
SamDeepThinking7 小时前
高并发场景下,CompletableFuture与ForkJoinPool该如何取舍?
java·后端·面试
张不才10 小时前
CPU 100% 了怎么办?Java 性能排障的标准化操作
java·后端
shepherd11112 小时前
吞吐量提升 10 倍:高并发大批量数据处理任务的架构演进与性能调优
java·后端·架构
plainGeekDev14 小时前
单例模式 → object 声明
android·java·kotlin
用户2986985301415 小时前
Java 实现 Word 文档文本与图片提取的方法
java·后端
SimonKing16 小时前
铁子,IntelliJ IDEA 2026.1.3来了,升不升?
java·后端·程序员
咖啡八杯1 天前
GoF设计模式——策略模式
java·后端·spring·设计模式
用户128526116021 天前
我把祖传Java项目重构后,接口响应从3s砍到了200ms,只改了这几行代码
java