首页 > 编程语言 >java解决表达式计算问题(转)

java解决表达式计算问题(转)

时间:2023-04-27 09:11:45浏览次数:39  
标签:java ops int nums else 运算符 计算 ans 表达式

这是LeetCode上的一道题,因为特别具有代表性,所有记录在这里。

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

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

你可以假设给定的表达式总是有效的。

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

 

示例 1:

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/basic-calculator-ii

题目的解法复制了宫水三叶的题解,由于我尚未完全理解,所以就直接贴过来。

题解

对于「任何表达式」而言,我们都使用两个栈 nums 和 ops:

nums : 存放所有的数字
ops :存放所有的数字以外的操作
然后从前往后做,对遍历到的字符做分情况讨论:

  • 空格 : 跳过
  • ( : 直接加入 ops 中,等待与之匹配的 )
  • ) : 使用现有的 nums 和 ops 进行计算,直到遇到左边最近的一个左括号为止,计算结果放到 nums
  • 数字 : 从当前位置开始继续往后取,将整一个连续数字整体取出,加入 nums
  • + - * / ^ % : 需要将操作放入 ops 中。在放入之前先把栈内可以算的都算掉(只有「栈内运算符」比「当前运算符」优先级高/同等,才进行运算),使用现有的 nums 和 ops 进行计算,直到没有操作或者遇到左括号,计算结果放到 nums

我们可以通过  来理解 只有「栈内运算符」比「当前运算符」优先级高/同等,才进行运算 是什么意思:

因为我们是从前往后做的,假设我们当前已经扫描到 2 + 1 了(此时栈内的操作为 + )。

如果后面出现的 + 2 或者 - 1 的话,满足「栈内运算符」比「当前运算符」优先级高/同等,可以将 2 + 1 算掉,把结果放到 nums 中;
如果后面出现的是 * 2 或者 / 1 的话,不满足「栈内运算符」比「当前运算符」优先级高/同等,这时候不能计算 2 + 1。
一些细节:

由于第一个数可能是负数,为了减少边界判断。一个小技巧是先往 nums 添加一个 0
为防止 () 内出现的首个字符为运算符,将所有的空格去掉,并将 (- 替换为 (0-,(+ 替换为 (0+(当然也可以不进行这样的预处理,将这个处理逻辑放到循环里去做)
从理论上分析,nums 最好存放的是 long,而不是 int。因为可能存在 大数 + 大数 + 大数 + … - 大数 - 大数 的表达式导致中间结果溢出,最终答案不溢出的情况

作者:AC_OIer
链接:https://leetcode.cn/problems/basic-calculator-ii/solution/shi-yong-shuang-zhan-jie-jue-jiu-ji-biao-c65k/
代码

class Solution {
    // 使用 map 维护一个运算符优先级
    // 这里的优先级划分按照「数学」进行划分即可
    Map<Character, Integer> map = new HashMap<>(){{
        put('-', 1);
        put('+', 1);
        put('*', 2);
        put('/', 2);
        put('%', 2);
        put('^', 3);
    }};
    public int calculate(String s) {
        // 将所有的空格去掉
        s = s.replaceAll(" ", "");
        char[] cs = s.toCharArray();
        int n = s.length();
        // 存放所有的数字
        Deque<Integer> nums = new ArrayDeque<>();
        // 为了防止第一个数为负数,先往 nums 加个 0
        nums.addLast(0);
        // 存放所有「非数字以外」的操作
        Deque<Character> ops = new ArrayDeque<>();
        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)) {
                    int u = 0;
                    int j = i;
                    // 将从 i 位置开始后面的连续数字整体取出,加入 nums
                    while (j < n && isNumber(cs[j])) u = u * 10 + (cs[j++] - '0');
                    nums.addLast(u);
                    i = j - 1;
                } else {
                    if (i > 0 && (cs[i - 1] == '(' || cs[i - 1] == '+' || cs[i - 1] == '-')) {
                        nums.addLast(0);
                    }
                    // 有一个新操作要入栈时,先把栈内可以算的都算了 
                    // 只有满足「栈内运算符」比「当前运算符」优先级高/同等,才进行运算
                    while (!ops.isEmpty() && ops.peekLast() != '(') {
                        char prev = ops.peekLast();
                        if (map.get(prev) >= map.get(c)) {
                            calc(nums, ops);
                        } else {
                            break;
                        }
                    }
                    ops.addLast(c);
                }
            }
        }
        // 将剩余的计算完
        while (!ops.isEmpty()) calc(nums, ops);
        return nums.peekLast();
    }
    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);
    }
    boolean isNumber(char c) {
        return Character.isDigit(c);
    }
}

作者:AC_OIer
链接:https://leetcode.cn/problems/basic-calculator-ii/solution/shi-yong-shuang-zhan-jie-jue-jiu-ji-biao-c65k/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

标签:java,ops,int,nums,else,运算符,计算,ans,表达式
From: https://www.cnblogs.com/wangbin2188/p/17357953.html

相关文章

  • 计算机网络----网络层
    《网络层概述》 来看一群网络,如果只是网络独立各自通信,那么只要实现物理层和数据链路层即可(一朵云中的多个节点通过交换机实现通信)如果想要实现这群网络之间的通信,则是网络层干的事情了(各个云之间通过路由器实现通信)《网络层需要解决的问题》  1.网络层提供两......
  • Java8使用Stream API转换Map遇到的2种异常报错和解决思路
    问题java8提供了StreamAPI,配合Lambda表达式,让开发者能对集合对象进行便利、高效的操作。在日常业务开发中,有个经常用到的场景是将List类型对象转换为Map类型对象,方便后续操作。在java8之前,这种转换需要先new一个Map对象,遍历list然后通过Map#put来初始化。使用java8后,可方便的......
  • java出现class lombok.javac.apt.LombokProcessor错误
    出现:java:java.lang.IllegalAccessError:classlombok.javac.apt.LombokProcessor(inunnamedmodule@0x3278991b)cannotaccessclasscom.sun.tools.javac.processing.JavacProcessingEnvironment(inmodulejdk.compiler)becausemodulejdk.compilerdoesnotexpor......
  • Java对象内存布局
    一、对象在堆内存中布局Objectobject=newObject()一般而言JDK8按照默认情况下,new一个对象占多少内存空间在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例数据(InstanceData)和对齐填充(Padding)。二、对象在堆内存中的存储布局下面......
  • java 并发编程-基础篇
    java创建线程的三种方法直接使用Thread//创建线程对象Threadt=newThread(){publicvoidrun(){//要执行的任务}};//启动线程t.start();Runable配合Thread把线程和任务分开。Runnablerunnable=newRunnable(){publicvoidrun(......
  • jsx中使用js表达式
    //在jsx中使用js表达式///通过一个{}展示变量即可vue中使用{{}}展示js表达式//什么是js表达式有结果的importreactDomfrom"react-dom"//函数也是表达式//syntaxError语法错误constsayHi=()=>{return"你好"}constspan=<span>我是一......
  • Java Lambda Stream
    ::方法使用条件:lambada表达式的主体仅包含一个表达式,且lambada表达式只调用一个已经存在的方法;被引用的方法的参数列表与lambada表达式的输入输出一致以下是Java8中方法引用的一些语法:静态方法引用(staticmethod)语法:classname::methodname例如:Person::getAge对象的实例方......
  • java线程之Semaphore
    Semaphore是信号量,用于线程间同步。例子,公交车上大概有24个座位,但是车上总共有40个人,那么,只要有一个人下车,另一个人就能得到一个座位,假设到终点站之前,每个人都能坐下。代码如下:packagecom.concurrent;importjava.util.Random;importjava.util.concurrent.CountDownLatch;imp......
  • java线程之FutureTask
    FutureTask是线程的异步计算。如果有多个线程,每个线程都要花很多时间计算,而且所花时间不同,最后要统计,就要用到此类。此类有个done方法,等call完后,执行此方法。代码:packagecom.concurrent;importjava.util.ArrayList;importjava.util.List;importjava.util.Random;importja......
  • java线程之wait、notifyAll
    wait、notifyAll是线程之间用来通信的,设计模式里的观察者模式。例子,上课前,同学在玩,一个同学观察老师是不是来了,如果来了,叫其他同学坐好。packagecom.concurrent;importjava.util.HashSet;importjava.util.Set;importjava.util.concurrent.CountDownLatch;importjava.util......