快手 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();
    }
}
相关推荐
Lhappy嘻嘻5 小时前
Java IO|File 文件操作 + 字节流 / 字符流完整笔记 + 递归删除文件实战
java·笔记·php
科技道人5 小时前
记录 默认置灰/禁用 app ‘Search Engine Selector‘ 的disable按钮
开发语言·前端·javascript
伊玛目的门徒5 小时前
试用leetcode之典中典 二数之和问题
java·算法·leetcode
逝水无殇8 小时前
C# 异常处理详解
开发语言·后端·c#
懒鸟一枚8 小时前
深入理解 Linux 内存、Swap 交换分区与分页机制的关系
java·linux·数据库
玖玥拾9 小时前
C# 语言进阶(十五)C# 游戏服务端 MySQL 数据库
服务器·开发语言·网络·数据库·mysql·c#
铅笔侠_小龙虾10 小时前
Rust 学习目录
开发语言·学习·rust
我命由我1234510 小时前
执行 Gradle 指令报错,无法将“grep”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
考虑考虑10 小时前
Sentinel安装
java·后端·微服务
云泽80810 小时前
从零吃透 C++ 异常:抛出捕获、栈展开、异常重抛与编码规范详解
开发语言·c++·代码规范