为运算表达式设计优先级

LC241. 为运算表达式设计优先级

给你一个由数字和运算符组成的字符串 expression ,按不同优先级组合数字和运算符,计算并返回所有可能组合的结果。你可以 按任意顺序 返回答案。

输入:expression = "2*3-4*5"
输出:[-34,-14,-10,-10,10]
解释:
(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

思路一:分治 + 记忆化递归

  1. 处理字符串,将数字和符号分别放到不同的数组中
  2. 分治:按符号进行遍历,以此符号为跟节点,两边递归去得到每边能得到的结果列表
  3. 用 map 存储结果链表,从而减少递归次数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
class Solution {
    HashMap<Integer, List<Integer>> map = new HashMap<>();
    List<Integer> numList = new ArrayList<>();
    List<Character> charList = new ArrayList<>();
    int K = 100001;
    public List<Integer> diffWaysToCompute(String ex) {
        // 处理字符串
        initList(ex);
        // 递归遍历
        return dfs(0, numList.size() - 1);
    }

    List<Integer> dfs(int l, int r) {
        List<Integer> res = new ArrayList<>();
        // 表示该部分只有一个数字,直接返回
        if (l == r) {
            res.add(numList.get(l));
            return res;
        }
        // 如果此部分在之前已经计算过,则直接返回结果集
        if (map.get(l * K + r) != null) {
            return map.get(l * K + r);
        }
        // 遍历每个符号,以符号为分割点,分成左右两部分
        for (int i = l; i < r; i++) {
            List<Integer> res1 = dfs(l, i), res2 = dfs(i+1, r);
            // 将得到的两个集合组合到一起
            for (int n1 : res1) {
                for (int n2 : res2) {
                    res.add(caculate(n1, charList.get(i), n2));
                }
            }
        }
        map.put(l * K + r, res);
        return res;
    }


    // 处理字符串
    void initList(String ex) {
        int n = ex.length(), i = 0;
        while (i < n) {
            if (isOperation(ex.charAt(i))) {
                charList.add(ex.charAt(i++));
            } else {
                int num = ex.charAt(i++) - '0';
                while (i < n && !isOperation(ex.charAt(i))) {
                    num = num * 10 + ex.charAt(i++) - '0';
                }
                numList.add(num);
            }
        }
    }

    int caculate(int num1, char c, int num2) {
        switch(c) {
            case '+':
                return num1 + num2;
            case '-':
                return num1 - num2;
            case '*':
                return num1 * num2;
        }
        return -1;
    }

    boolean isOperation(char c) {
        return c == '+' || c == '-' || c == '*';
    }
}

思路二:动态规划

  1. 处理字符串,将数字和符号分别放到不同的数组中
  2. 创建 dp[][] 数组,dp[i][j] 表示从第 i 个数字到第 j 个数字直接能产生的结果集
  3. 以长度为切入点,当只有一个数字时,dp[i][i],一直遍历到有 n 个数字
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
class Solution {
    HashMap<Integer, List<Integer>> map = new HashMap<>();
    List<Integer> numList = new ArrayList<>();
    List<Character> charList = new ArrayList<>();
    int K = 100001;
    public List<Integer> diffWaysToCompute(String ex) {
        // 处理字符串
        initList(ex);
        int n = numList.size();
        // 创建 dp
        ArrayList<Integer>[][] dp = (ArrayList<Integer>[][]) new ArrayList[n][n];
        // 当长度为 1 时
        for (int i = 0; i < n; i++) {
            ArrayList<Integer> list = new ArrayList<>();
            list.add(numList.get(i));
            dp[i][i] = list;
        }
        // 长度从 2 到 n 个数字
        for (int k = 1; k < n; k++) {
            // 开始下标
            for (int i = 0; i < n - k; i++) {
                // 结束下标
                int j = i + k;
                ArrayList<Integer> res = new ArrayList<>();
                // 遍历分割点
                for (int x = i; x < j; x++) {
                    ArrayList<Integer> res1 = dp[i][x], res2 = dp[x+1][j];
                    //System.out.println(i + " " + j + " " + x);
                    for (int n1 : res1) {
                        for (int n2 : res2) {
                            res.add(caculate(n1, charList.get(x), n2));
                        }
                    }
                }
                dp[i][j] = res;
            }
        }
        return dp[0][n-1];
    }
    

    void initList(String ex) {
        int n = ex.length(), i = 0;
        while (i < n) {
            if (isOperation(ex.charAt(i))) {
                charList.add(ex.charAt(i++));
            } else {
                int num = ex.charAt(i++) - '0';
                while (i < n && !isOperation(ex.charAt(i))) {
                    num = num * 10 + ex.charAt(i++) - '0';
                }
                numList.add(num);
            }
        }
    }

    int caculate(int num1, char c, int num2) {
        switch(c) {
            case '+':
                return num1 + num2;
            case '-':
                return num1 - num2;
            case '*':
                return num1 * num2;
        }
        return -1;
    }

    boolean isOperation(char c) {
        return c == '+' || c == '-' || c == '*';
    }
}
updatedupdated2022-06-232022-06-23