表达式求值(from leetcode 241)

给定一个正确的表达式(不用担心不规范的输入),比如2-1-1, 通过在不同位置添加左右括号,改变求值的优先级顺序,求出所有的这些值;

Example 1

Input: "2-1-1".

((2-1)-1) = 0
(2-(1-1)) = 2

Output: [0, 2]

Example 2

Input: "2*3-4*5"

(2*(3-(4*5))) = -34
((2*3)-(4*5)) = -14
((2*(3-4))*5) = -10
(2*((3-4)*5)) = -10
(((2*3)-4)*5) = 10

Output: [-34, -14, -10, -10, 10]

这个题目应该没有便捷的解法,只能全部的都尝试一遍;当然仅仅需要暴力的枚举一遍,也没有什么有趣的地方,我觉得有趣的地方体现在下面两点:

1. 这个题目可以用递归的方法求解,因为,假设遇到某个操作符,如果知道了左边的结果,再计算出右边的结果,那么只要把左右两边的结果合并起来,就可以了;

2. 当然如果直接按照递归去做,会出现一个问题,(大概会超时,我没有提交这样的代码);假设在得到了某个操作符两边的结果后,到了下一个操作符,递归计算的时候,任然会需要前面一个操作符(左边的)的结果,所以必须要把已经计算过的结果要cache起来;

最后的代码如下:

private final static long base = 100001;
private Map<Long, List<Integer>> cache = new HashMap<>();

private List<Integer> diffWaysToCompute(char[] cs, int start, int end) {
    long key = base * start + end;
    if (cache.containsKey(key)) {
        return cache.get(key);
    }

    boolean isNumber = true;

    for (int i = start; i < end; i++) {
        if (isOperator(cs[i])) {
            isNumber = false;
            break;
        }
    }

    List<Integer> result = new ArrayList<>();
    if (isNumber) {
        result.addAll(toNum(cs, start, end));
    } else {

        for (int i = start; i < end; i++) {
            if (isOperator(cs[i])) {
                List<Integer> prev = diffWaysToCompute(cs, start, i);
                List<Integer> suff = diffWaysToCompute(cs, i + 1, end);
                result.addAll(combineResult(prev, suff, cs[i]));
            }
        }

        return result;
    }

    cache.put(key, result);
    return result;
}

private List<Integer> combineResult(List<Integer> prev, List<Integer> suff, char op) {
    List<Integer> result = new ArrayList<>();

    for (int x : prev) {
        for (int y : suff) {
            result.add(calculate(x, y, op));
        }
    }

    return result;
}

private int calculate(int x, int y, char op) {
    switch (op) {
        case ‘+‘:
            return x + y;
        case ‘-‘:
            return x - y;
        case ‘*‘:
            return x * y;
    }
    return 0;
}

private List<Integer> toNum(char[] cs, int start, int end) {
    int num = 0;

    for (int i = start; i < end; i++) {
        if (cs[i] == ‘ ‘) {
            continue;
        }
        num = num * 10 + (cs[i] - ‘0‘);
    }
    List<Integer> result = new ArrayList<>(1);
    result.add(num);
    return result;
}

private boolean isOperator(char c) {
    return c == ‘+‘ || c == ‘-‘ || c == ‘*‘;
}

public List<Integer> diffWaysToCompute(String input) {
    return diffWaysToCompute(input.toCharArray(), 0, input.length());
}

public static void main(String[] args) {
    Solution solution = new Solution();
    System.out.println(solution.diffWaysToCompute("2-4").stream().map(x -> "" + x).collect(Collectors.joining(",")));
}

再仔细想想,这种需要cache之前结果的递归算法,应该是可以用动态规划的方式表达出来的。可惜我不擅长动态规划,就不费脑力了~~~

时间: 2024-10-09 23:27:04

表达式求值(from leetcode 241)的相关文章

LeetCode:逆波兰表达式求值【150】

LeetCode:逆波兰表达式求值[150] 题目描述 根据逆波兰表示法,求表达式的值. 有效的运算符包括 +, -, *, / .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 说明: 整数除法只保留整数部分. 给定逆波兰表达式总是有效的.换句话说,表达式总会得出有效数值且不存在除数为 0 的情况. 示例 1: 输入: ["2", "1", "+", "3", "*"] 输出: 9 解释: ((2

数据结构算法C语言实现(八)--- 3.2栈的应用举例:迷宫求解与表达式求值

一.简介 迷宫求解:类似图的DFS.具体的算法思路可以参考书上的50.51页,不过书上只说了粗略的算法,实现起来还是有很多细节需要注意.大多数只是给了个抽象的名字,甚至参数类型,返回值也没说的很清楚,所以很多需要自己揣摩.这也体现了算法和程序设计语言的特点,算法更侧重本质的描述,而任何编程语言都要照顾到实现的细节以及数据类型等语法方面的需求. 表达式求值: [编码中....] 二.头文件 迷宫求解: 1 //3_2_maze.h 2 /** 3 author:zhaoyu 4 email:[em

表达式求值 (栈) 用C++实现

1 #include <cstdio> 2 3 #include <cstdlib> 4 5 #include <cmath> 6 7 #include <stack> 8 9 #include <cstring> 10 11 using namespace std; 12 13 14 15 char Precede(char a, char b) { //判断运算符优先级 16 17 int i, j; 18 19 char Table[8][

中缀表达式求值总结

中缀表达式的题目困扰了我两三年,都没去写过..这两天看到2005年提高组的T3要用到所以心血来潮写了一下. 表达式求值借助基本结构应该不用说了是栈,不管手写还是STL都没有太大关系.而中缀表达式最难控制的地方是优先级,算上+-*/^()一共有四个优先级[+-,*/,, ^()](后面会提到一个三级的字符“负号”,这是预留空位). 下面对一个例子进行分析:2*3+4*6/3+17-4^2 那么处理的时候怎么控制优先级呢? 搜到‘+’之前,栈的情况是这样的: 操作数栈:2  3 操作符栈:* 然后搜

四则运算表达式求值 OpenJ_Bailian - 4132

四则运算表达式求值 OpenJ_Bailian - 4132 题意:设计一个计算器,实现+-*/以及()的表达式运算求值. 栈的应用,这学期学数据结构,手写了栈练一下~ 1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn=10010; //最大表达式长度 4 5 template <typename T> 6 class Stack; 7 8 template <typename T> 9

表达式求值

表达式求值 时间限制:3000 ms  |  内存限制:65535 KB 难度:3 描写叙述 Dr.Kong设计的机器人卡多掌握了加减法运算以后,近期又学会了一些简单的函数求值,比方,它知道函数min(20,23)的值是20 ,add(10,98) 的值是108等等.经过训练,Dr.Kong设计的机器人卡多甚至会计算一种嵌套的更复杂的表达式. 如果表达式能够简单定义为: 1. 一个正的十进制数 x 是一个表达式. 2. 假设 x 和 y 是 表达式,则 函数min(x,y )也是表达式,其值为x

四则运算表达式求值の各种心碎

实验三---四则运算表达式求值 一.基本要求: ( 1 ) 利用二叉树后序遍历来实现表达式的转换,同时可以使用实验三的结果来求解后缀表达式的值. ( 2) 输入输出格式: 输入格式:在字符界面上输入一个中缀表达式,回车表示结束. 请输入表达式: 输入一个中缀表达式 输出格式:如果该中缀表达式正确,那么在字符界面上输出其后缀表达式,其中后 缀表达式中两相邻操作数之间利用空格隔开:如果不正确,在字符界面上输出表达式错误提示. 逆波兰表达式为: 输出逆波兰表达式 运算结果为:输出运算后的结果 测试数据

lintcode 中等题:Evaluate Reverse Polish notation逆波兰表达式求值

题目 逆波兰表达式求值 在逆波兰表达法中,其有效的运算符号包括 +, -, *, / .每个运算对象可以是整数,也可以是另一个逆波兰计数表达. 样例 ["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9 ["4", "13", "5", "/", "+"]

使用逆波兰式进行表达式求值

中缀表达式及后缀表达式图解中说明了使用逆波兰式进行表达式求值的方法,这里使用C++进行实现.实现和原理讲解有一点不同,需要进一步进行细化. 关于将中缀表达式转换成后后缀表达式的规则: 规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分:若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于找顶符号(乘除优先加减)则栈顶元素依次出找并输出,并将当前符号进栈,一直到最终输出后缀表达式为止. 上面的规则转换成下面的执行规则: 1.遇到操作数:直接输出(添加到后缀