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大佬的解法,我们可以从数据量小的时候开始分析,这样会比较简单,然后再深入,这道题默认输入的表达式是有效的。
- 如果input只有两个数字及一个运算符,加不加括号都没影响,直接计算返回
- 我们先观察比较简单的实例1,"2-1-1",遇到运算符,我们可以选择加括号或者不加括号,那么此时这个变成了 “2-()” 和 “()-1”,往普遍推广一下就是 “() 运算符 ()”
- 在每个运算符的地方,将 input 分成左右两部分,从而扔到递归中去计算,从而可以得到两个整型数组 left 和 right,分别表示作用两部分各自添加不同的括号所能得到的所有不同的值,此时我们只要分别从两个数组中取数字进行当前的运算符计算,然后把结果存到 res 中即可。分成左右两个部分的方法就是“分治”了。
- 当然,若最终结果 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;
}
}
若返回的是有几种可能,那么就是卡塔兰数。
- 当值为0时,1种可能;
- 当值为1时,也是1种可能;
- 当值为2时,1和2都能为根,h(2) = h(0)*h(1) + h(1)*h(0)
- 值为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];
}