表達式求值(from leetcode 241)

給定一個正確的表達式(不用擔憂不規範的輸入),好比2-1-1, 經過在不一樣位置添加左右括號,改變求值的優先級順序,求出全部的這些值;java

Example 1算法

Input: "2-1-1".spa

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

Output: [0, 2]code

Example 2orm

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]get

這個題目應該沒有便捷的解法,只能所有的都嘗試一遍;固然僅僅須要暴力的枚舉一遍,也沒有什麼有趣的地方,我以爲有趣的地方體如今下面兩點:input

1. 這個題目能夠用遞歸的方法求解,由於,假設遇到某個操做符,若是知道了左邊的結果,再計算出右邊的結果,那麼只要把左右兩邊的結果合併起來,就能夠了;it

2. 固然若是直接按照遞歸去作,會出現一個問題,(大概會超時,我沒有提交這樣的代碼);假設在獲得了某個操做符兩邊的結果後,到了下一個操做符,遞歸計算的時候,任然會須要前面一個操做符(左邊的)的結果,因此必需要把已經計算過的結果要cache起來;io

最後的代碼以下:

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以前結果的遞歸算法,應該是能夠用動態規劃的方式表達出來的。惋惜我不擅長動態規劃,就不費腦力了~~~

相關文章
相關標籤/搜索