LeetCode刷题之分治

925 阅读4分钟

241. 为运算表达式设计优先级(Medium)

给定一个含有数字和运算符的字符串,为表达式添加括号,改变其运算优先级以求出不同的结果。你需要给出所有可能的组合的结果。有效的运算符号包含 +, - 以及 * 。

示例 1:

输入: "2-1-1"
输出: [0, 2]
解释:
((2-1)-1) = 0 (2-(1-1)) = 2

示例 2:

输入: "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.需要将添加符号之后的表达式计算出来并存入数组返回。参考Grandyang大佬的解法,我们可以从数据量小的时候开始分析,这样会比较简单,然后再深入,这道题默认输入的表达式是有效的。

  1. 如果input只有两个数字及一个运算符,加不加括号都没影响,直接计算返回
  2. 我们先观察比较简单的实例1,"2-1-1",遇到运算符,我们可以选择加括号或者不加括号,那么此时这个变成了 “2-()” 和 “()-1”,往普遍推广一下就是 “() 运算符 ()”
  3. 在每个运算符的地方,将 input 分成左右两部分,从而扔到递归中去计算,从而可以得到两个整型数组 left 和 right,分别表示作用两部分各自添加不同的括号所能得到的所有不同的值,此时我们只要分别从两个数组中取数字进行当前的运算符计算,然后把结果存到 res 中即可。分成左右两个部分的方法就是“分治”了。
  4. 当然,若最终结果 res 中还是空的,那么只有一种情况,input 本身就是一个数字,直接转为整型存入结果 res 中即可
class Solution {
    public List<Integer> diffWaysToCompute(String input) {

        List<Integer> result = new ArrayList<>();

        //不包括运算符,直接返回
        if (!input.contains("+") && !input.contains("-") && !input.contains("*")) {
            result.add(Integer.valueOf(input));
            return result;
        }

        //分治
        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            //遇到运算符将式子分成左右两部分
            if (c == '+' || c == '-' || c == '*') {
                //由递归得到运算符左右两边的结果
                List<Integer> left = diffWaysToCompute(input.substring(0, i));
                List<Integer> right = diffWaysToCompute(input.substring(i + 1));
                //分别从left和right取数进行运算
                for (int j = 0; j < left.size(); j++) {
                    for (int k = 0; k < right.size(); k++) {
                        if (c == '+'){
                            result.add(left.get(j) + right.get(k));
                        }else if (c == '-'){
                            result.add(left.get(j) - right.get(k));
                        }else {
                            result.add(left.get(j) * right.get(k));
                        }
                    }
                }
            }
        }
        return result;
    }
}

不同的二叉搜索树 II(Medium)

给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树。

示例:

输入: 3
输出:
[
  [1,null,3,2],
  [3,2,null,1],
  [3,1,null,null,2],
  [2,1,3],
  [1,null,2,null,3]
]

题解:首先分析回顾一下什么是二叉搜索树:每个节点的左子树上的值都比它小,右子树上的值都比它大。

给定一个整数n,1~n都可以作为根节点root,root左边的值作为左子树,右边的值作为右子树,然后在左右子树上继续这么划分,这样问题就又变成了“分治”。

class Solution {
    public List<TreeNode> generateTrees(int n) {

        if (n < 1){
            return new LinkedList<TreeNode>();
        }
        return generateSubTrees(1, n);
    }

    //生成子树
    private List<TreeNode> generateSubTrees(int s, int e){
        List<TreeNode> res = new LinkedList<TreeNode>();

        //递归结束条件
        if (s > e){
            res.add(null);
            return res;
        }

        //所有节点都可以当根节点
        for (int i = s; i <= e; i++) {
            //生成左子树和右子树
            List<TreeNode> leftSubtrees = generateSubTrees(s, i - 1);
            List<TreeNode> rightSubtrees = generateSubTrees(i + 1, e);
            //左子树和右子树的每个节点又可以作为当前的根节点
            for (TreeNode left : leftSubtrees) {
                for (TreeNode right : rightSubtrees) {
                    TreeNode root = new TreeNode(i);
                    root.left = left;
                    root.right = right;
                    res.add(root);
                }
            }
        }
        return res;
    }
}

若返回的是有几种可能,那么就是卡塔兰数。

  1. 当值为0时,1种可能;
  2. 当值为1时,也是1种可能;
  3. 当值为2时,1和2都能为根,h(2) = h(0)*h(1) + h(1)*h(0)
  4. 值为3时, h(3) = h(0) * h(2)   (1为根的情况,则左子树一定不存在,右子树可以有两个数字)

 + h(1) * h(1)   (2为根的情况,则左右子树都可以各有一个数字)

+ h(2) * h(0)   (3为根的情况,则左子树可以有两个数字,右子树一定不存在)

public int generateTrees(int n) {
        int[] dp = new int[n + 1];
        dp[0] = dp[1] = 1;
        for (int i = 2; i <= n; ++i) {
            for (int j = 0; j < i; ++j) {
                dp[i] += dp[j] * dp[i - j - 1];
            }
        }
        return dp[n];
    }