题目描述
给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树。
示例:
输入: 3 输出: 5 解释: 给定 n = 3, 一共有 5 种不同结构的二叉搜索树: 1 3 3 2 1 \ / / / \ \ 3 2 1 1 3 2 / / \ \ 2 1 2 3
递归思路
1.二叉搜索树的性质是根节点的左子树都比根节点小,根节点的右子树都比根节点大。
2.可以从二叉搜索树的性质看出,其定义与递归可以完美的契合。
3.我们可以编写一个函数,当我们选定i作为根节点时,function(start,i-1)为所有左子树数量,function(i+1,end)为所有右子树数量,二者的结果组合(相乘),便是以i作为根节点的所有二叉搜索树。
Java代码实现
public int numTrees(int n){ if(n<1) return n; return numTrees(1,n); } public int numTrees(int start,int end){ if(start > end) return 1; int sum = 0; for (int i = start; i <= end ; i++) { int left = numTrees(start,i-1); int right = numTrees(i+1,end); sum = sum + left*right; } return sum; }
备忘录思路
1.我们可以发现存在节点的答案被计算了多次,所以可以使用一个备忘录,若该节点的答案已经被计算过了,便直接返回。
Java代码实现
public int numTrees(int n){ if(n<1) return n; Map<String,Integer> memory = new HashMap<>(); return numTrees(1,n,memory); } public int numTrees(int start,int end,Map<String,Integer> memory){ if(start > end) return 1; if(memory.containsKey(start+"-"+end)) return memory.get(start+"-"+end); int sum = 0; for (int i = start; i <= end ; i++) { int left = numTrees(start,i-1,memory); int right = numTrees(i+1,end,memory); sum = sum + left*right; } memory.put(start+"-"+end,sum); return sum; }
动态规划思路
- 我们通过以上的思路可以发现,这道题的套路和动态规划是一致的(递归->备忘录->动态规划),所以我们只需要找到状态转移方程,便可以使用动态规划求解,大幅度降低执行时间。
2.下面我们进行状态转移方程的推导。
- 假设我们有n个节点,那么他的组合个数应该等于1~n个节点当根节点的组合数总和,即:G(n) = f(1)+f(2)+f(3)+......+f(n)
- 我们从递归的方法中可以发现:f(i) = G(i-1) + G(n-i)
- 将两个公式结合后:G(n) = G(0)G(n-1)+G(1)G(n-2)+.....+G(n-1)G(0)
其实这个公式就是大名鼎鼎的卡特兰数,有兴趣的同学可以去了解一下。
Java代码实现
public int numTrees(int n){ int[] dp = new int[n+1]; dp[0] = 1; dp[1] = 1; for (int i = 2; i <=n ; i++) { for (int j = 1; j <=i ; j++) { dp[i] += dp[j-1]*dp[i-j]; } } return dp[n]; }