牛客-NC137-表达式求值

NC137. 表达式求值(medium)


在这里插入图片描述

方法一:双栈法(表达式计算的通用解法)

思路:这里借鉴了三叶大佬的题解,自己在这写一遍加深印象。
对于任何表达式而言,我们可以使用两个栈nums和ops:

  • nums:存放所有的数字
  • ops:存放所有的数字之外的操作
    然后从前往后遍历,对遍历的字符分情况讨论:
  • 空格:跳过
  • (:直接加入ops中,等待与之匹配的)
  • ):使用现有的numsops进行计算,直到遇到左边最近的一个左括号为止,计算结果放到nums
  • 数字:从当前位置开始继续往后取,将一个连续数字整体取出,加入nums
  • + - * / ^ %:需要将操作放入ops中。在放入之前先把栈内可以算的都算掉(只有栈内运算符当前运算符优先级高/同等,才进行运算),使用现有的numsops进行计算,直到没有操作或遇到左括号,计算结果放到nums
    另外,还有一些特殊情况需要考虑:
  • 对于空格,我们先将这些空格替换掉
  • 由于第一个数可能是负数,我们可以先往nums中添加一个0以减少边界判断
  • 为防止()内出现的首个字符为运算符,将所有的空格去掉,并将(-替换为(0-(+替换为(0+
  • 为防止中间结果溢出,可以将nums中存放为long而不是int
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 返回表达式的值
     * @param s string字符串 待计算的表达式
     * @return int整型
     */
    // 优先级维护表
    static Map<Character, Integer> map = new HashMap<>();
    // 数字栈
    static Deque<Integer> nums = new ArrayDeque<>();
    // 操作栈
    static Deque<Character> ops = new ArrayDeque<>();
    public int solve (String s) {
        // write code here
                // 细节处理
        s = s.replaceAll(" ", "");
        s.replaceAll("\\(-","\\0-");
        s.replaceAll("\\(+","\\0+");
        map.put('-', 1);
        map.put('+', 1);
        map.put('*', 2);
        map.put('/', 2);
        map.put('%', 2);
        map.put('^', 3);
        char[] cs = s.toCharArray();
        int n = s.length();
        nums.addLast(0);
        for (int i = 0; i < n; i++) {
            char c = cs[i];
            if (c == '(') {
                // 左括号直接加入
                ops.addLast(c);
            } else if (c == ')') {
                // 遇到右括号
                while (!ops.isEmpty()) {
                    if (ops.peekLast() != '(') {
                        // 没遇到左括号,我就一直取数做加减乘除法
                        calc(nums, ops);
                    } else {
                        // 遇到左括号,结束了!
                        ops.pollLast();
                        break;
                    }
                }
            } else {
                if (isNumber(c)) {
                    // 遇到数字(可能是一个很长的数字,比如123456这样的)
                    int u = 0;
                    int j = i;
                    while (j < n && isNumber(cs[j])) {
                        u = u * 10 + (cs[j++] - '0');
                    }
                    // 将这个数字存到nums栈中
                    nums.addLast(u);
                    i = j - 1; // 此时j在一个非数字的位置
                } else {
                    // 遇到加减乘除运算操作
                    while (!ops.isEmpty() && ops.peekLast() != '(') {
                        char prev = ops.peekLast();
                        if (map.get(prev) >= map.get(c)) {
                            // 栈中的操作符优先级高于当前遍历到的优先级
                            calc(nums,ops);
                        } else {
                            // 比栈中的优先级高
                            break;
                        }
                    }
                    // 将当前遍历到的优先级放到ops栈中
                    ops.addLast(c);
                }
            }
        }
        while (!ops.isEmpty() && ops.peekLast() != '(') {
            calc(nums, ops);
        }
        return nums.peekLast();
    }
    static boolean isNumber(char c) {
        return Character.isDigit(c);
    }
    static void calc(Deque<Integer> nums, Deque<Character> ops) {
        // 没有数或者只有一个数或者没有操作数
        if (nums.isEmpty() || nums.size() < 2) return;
        if (ops.isEmpty()) return;
        int b = nums.pollLast(), a = nums.pollLast();
        char op = ops.pollLast();
        int ans = 0;
        if (op == '+') {
            ans = a + b;
        } else if (op == '-') {
            ans = a - b;
        } else if (op == '*') {
            ans = a * b;
        } else if (op == '/') {
            ans = a / b;
        } else if (op == '^') {
            ans = (int)Math.pow(a, b);
        } else if (op == '%') {
            ans = a % b;
        }
        nums.addLast(ans);
    }
}

时间复杂度: O(N),遍历一遍字符串即可。
空间复杂度: O(N),需要使用双栈,但双栈的长度之和为字符串长度。
总结:这道题好难啊,做了两遍都没写出来,三月份写过一遍,现在写还是写不出来。还是得温故而知新啊,并且这种解法可以秒杀所有的关于计算器设计和表达式求值的题目。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值