mskitten

又惘又怠

95. Unique Binary Search Trees II

题目

Given an integer n, generate all structurally unique BST's (binary search trees) that store values 1 ... n.

Example:

Input: 3
Output:
[
  [1,null,3,2],
  [3,2,null,1],
  [3,1,null,null,2],
  [2,1,3],
  [1,null,2,null,3]
]
Explanation:
The above output corresponds to the 5 unique BST's shown below:

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

思路

记fn为1至n组成的BST的集合。对该BST,根的值有n种情况。对每一个根值i,左树为1至i-1,即问题fi-1。右树为i+1至n,即问题fn-i,但每个节点要+i。即

fn = ∑ fi-1 * fn-i (i=1...n)

注: 此处答案要求是一个List,所以*不代表数学的乘积而是将左右树做交叉结合

代码

 1 class Solution {
 2     public List<TreeNode> generateTrees(int n) {
 3         List<TreeNode>[] dp = new List[n + 1];
 4         dp[0] = new ArrayList<>();
 5         if(n == 0) return dp[0];
 6         dp[0].add(null);
 7         
 8         for(int i = 1; i <= n; i++){
 9             dp[i] = new ArrayList<>();
10             for(int j = 1; j <= i; j++){
11                 int left = j - 1, right = i - j;
12                 for(TreeNode leftTree : dp[left])
13                     for(TreeNode rightTree : dp[right]){
14                         TreeNode root = new TreeNode(j);
15                         root.left = clone(leftTree, 0);
16                         root.right = clone(rightTree, j);
17                         dp[i].add(root);
18                     }
19             }
20         }
21         return dp[n];
22     }
23     
24     private TreeNode clone(TreeNode x, int offset){
25         if(x == null)
26             return null;
27         TreeNode result = new TreeNode(x.val + offset);
28         result.left = clone(x.left, offset);
29         result.right = clone(x.right, offset);
30         return result;
31     }
32 }

复杂度(大致分析)

设问题n对应的List长度为ln,由于每次都从1到n开始填表,则问题n的时间复杂度Tn大致为

Tn = 1*l1 + 2*l2 + ...+ n*ln   (1)

其中乘以n的原因是:ln中的每一颗树都是通过克隆子问题的树形成的。对于一个n个节点的树,克隆的时间为O(n)。

对于每一个ln,考虑到其左右子树的不同大小,又有

l= ∑ lj-1 * ln-j (j=1...n)   (2)

即l2 = l0*l1 + l1*l0, l3 = l0*l2 + l1*l1 + l2*l0,...

n 0 1 2 3 4 5 6 ...
ln 1 1 2 5 14 42 ... ...
这是一个卡塔兰数(Catalan Number),我们只要知其结果即可。结果是ln = (2n)!/((n+1)!(n)!)。
 
这个链接声称卡塔兰数的和满足
SN204N9eπN3/2.
SN204N9eπN3/2.

我们的和里还有一个因子n(式1),大致可以确定Tn大概为O(4n/nk)的样子吧,至于k是几不会算(据上式,起码有k<3/2...)……

实际上,卡塔兰数的渐进表示大致为

与上述估算的Tn大致相同(最大的差别在于那个clone所耗费的因子n),也就是说dp算法本身比穷举法的时间复杂度差一点,拖了一点后腿……

不过这个问题用穷举法确实没想到intuitive的算法,backtracking的话估计浪费更严重,dp应该算是比较简洁又复杂度还可以的算法……

 

posted @ 2018-09-10 18:12  mskitten  阅读(361)  评论(0编辑  收藏  举报