当前位置: 首页 > article >正文

【表达式求值算法】拆解复杂问题:实现计算器

        表达式求值算法是个 Hard 级别的问题,本次就来解决它。最终实现一个包含如下功能的计算器:

        1.输入一个字符串,可以包含 + - * / 、数字、括号以及空格,算法返回运行结果。

        2.要符合运算规则,括号的优先级最高,先乘除后加减。

        3.除号是整数除法,无论正负都向0取整(5/2=2,-5/2=-2)。

        4.可以假定输入的算式一定合法,且计算过程不会出现整型溢出,不会出现除数为0的意外情况。

        比如输入如下字符串,算法会返回9:

        “3 * (2-6 / (3 - 7))”

        其关键在于层层拆解问题,化整为零,逐个击破,相信这种思维方式能帮助大家解决各种复杂问题。

        下面就来拆解,先从最简单的一个问题开始。

处理加减法

        如果输入的算式只包含加减法,而且不存在空格,以字符串算式“1-12+3”为例,来说一个很简单的思路:

        1.先给第一个数字加一个默认符号“+”,变成“+1-12+3”。

        2.把一个运算符和数字组成一对,也就是三对 +1,-12,+3,把它们转化成数字,然后放到一个栈中。

        3.将栈中所有的数字求和,就是原算式的结果。

        直接看代码:

import java.util.Stack;

public class Calculate {
    public int calculate(String s) {
        Stack<Integer> stack = new Stack();
        // 记录 num 前的符号,初始化为+
        char sign = '+';
        // 记录算式中的数字
        int num = 0;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            // 如果是数字,连续读取出来
            if (c >= '0' && c <= '9') {
                num = 10 * num + (c - '0');
            }
            // 如果不是数字,就是遇到了下一个符号,之前的数字和符号就要入栈
            if ((c < '0' || c > '9') || i == s.length() - 1) {
                switch (sign) {
                    case '+':
                        stack.push(num);
                        break;
                    case '-':
                        stack.push(-num);
                        break;
                }
                // 更新符号为当前符号,数字清零
                sign = c;
                num = 0;
            }
        }
        // 将栈中所有结果求和就是答案
        int res = 0;
        while (!stack.empty()) {
            res += stack.pop();
        }
        return res;
    }

    public static void main(String[] args) {
        Calculate calculate = new Calculate();
        int res = calculate.calculate("1-12+3");
        System.out.println(res);
    }
}

 处理乘除法

        思路和仅处理加减法没什么区别,拿字符串“2-3*4+5”举例,核心思路依然是把字符串分解成符号和数字的组合。

        比如上述例子就可以分解为 +2,-3,*4,+5几对,其他部分都不用变,在 switch 部分加上对应的 case 就行了。乘除法优先于加减法体现在:乘除法可以和栈顶的数结合然后把结果加入栈;而加减法只能把自己放入栈。空格不是运算符,加个条件直接忽略就行:

import java.util.Stack;

public class Calculate {
    public int calculate(String s) {
        Stack<Integer> stack = new Stack();
        // 记录 num 前的符号,初始化为+
        char sign = '+';
        // 记录算式中的数字
        int num = 0;
        // 记录栈顶元素
        int pre = 0;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            // 如果是数字,连续读取出来
            if (c >= '0' && c <= '9') {
                num = 10 * num + (c - '0');
            }
            // 如果不是数字,就是遇到了下一个符号,之前的数字和符号就要入栈
            if ((c < '0' || c > '9') && c != ' ' || i == s.length() - 1) {
                switch (sign) {
                    case '+':
                        stack.push(num);
                        break;
                    case '-':
                        stack.push(-num);
                        break;
                    case '*':
                        pre = stack.pop();
                        stack.push(pre * num);
                        break;
                    case '/':
                        pre = stack.pop();
                        stack.push(pre / num);
                        break;
                }
                // 更新符号为当前符号,数字清零
                sign = c;
                num = 0;
            }
        }
        // 将栈中所有结果求和就是答案
        int res = 0;
        while (!stack.empty()) {
            res += stack.pop();
        }
        return res;
    }


    public static void main(String[] args) {
        Calculate calculate = new Calculate();
        int res = calculate.calculate("2-3*4-5");
        System.out.println(res);
    }
}

处理括号

        这里就到难点了,但其实一点都不难,因为括号具有递归性质,无论括号嵌套多少层,都可以用一个递归搞定。括号包含的算式,直接视为一个数字就行了。(leetcode例题:224.基本计算器)

        话不多说,直接上代码:

import java.util.Stack;

public class Calculate {
    public int calculate(String s) {
        Stack<Character> stack = new Stack();
        for (int i = s.length() - 1; i >= 0; i--) {
            stack.push(s.charAt(i));
        }
        return helper(stack);
    }

    public int helper(Stack<Character> stack) {
        Stack<Integer> nums = new Stack<>();
        // 记录 num 前的符号,初始化为+
        char sign = '+';
        // 记录算式中的数字
        int num = 0;
        // 记录栈顶元素
        int pre = 0;
        while (!stack.empty()) {
            char c = stack.pop();
            if (c >= '0' && c <= '9') {
                num = 10 * num + (c - '0');
            }
            // 遇到左括号开始递归计算
            if (c == '(') {
                num = helper(stack);
            }
            // 如果不是数字,就是遇到了下一个符号,之前的数字和符号就要入栈
            if ((c < '0' || c > '9') && c != ' ' || stack.empty()) {
                switch (sign) {
                    case '+':
                        nums.push(num);
                        break;
                    case '-':
                        nums.push(-num);
                        break;
                    case '*':
                        pre = stack.pop();
                        nums.push(pre * num);
                        break;
                    case '/':
                        pre = stack.pop();
                        nums.push(pre / num);
                        break;
                }
                // 更新符号为当前符号,数字清零
                sign = c;
                num = 0;
            }
            if (c == ')') {
                break;
            }
        }
        // 将栈中所有结果求和就是答案
        int res = 0;
        while (!nums.empty()) {
            res += nums.pop();
        }
        return res;
    }
}


http://www.kler.cn/a/324424.html

相关文章:

  • 如何编译 Cesium 源码
  • Spring:bean的配置
  • 向潜在安全信息和事件管理 SIEM 提供商提出的六个问题
  • 坚果云·无法连接服务器(无法同步)
  • 动态内存管理(c语言)
  • 设计模式(四)装饰器模式与命令模式
  • 17.第二阶段x86游戏实战2-线程发包和明文包
  • Python近红外光谱数据分析
  • 几个将ppt文件压缩变小的方法!
  • [CKA]CKA预约和考试
  • 产品包装检测系统源码分享
  • OpenGL ES简述(1)
  • 如何使用 WebRTC 获取摄像头视频
  • 组播基础-2-IGMP协议
  • ★ C++进阶篇 ★ map和set
  • 个人健康管理小程序(源码+参考文档+定制)
  • python中序列化和反序列化
  • 一步一步优化一套生成式语言模型系统
  • docker简介、安装、基础知识
  • 基于webComponents的纯原生前端框架
  • Xcode 16 上传AppStore遇到第三方库 bitcode 的问题
  • Python爬虫bs4基本使用
  • Java编程基础:类与对象的探索之旅
  • C++学习笔记----8、掌握类与对象(一)---- 对象中的动态内存分配(6)
  • 【球形空间产生器】
  • 解决 Java 中由于 parallelStream 导致的死锁