LintCode刷题——不同的二叉查找树I、II

不同的二叉查找树I:

题目内容:

给出 n,问由 1...n 为节点组成的不同的二叉查找树有多少种?

样例:

给出n = 3,有5种不同形态的二叉查找树:

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

算法分析:

先来看一下二叉查找树的特点,当选定一个节点i作为中间节点时:

  ①位于该节点左子树中的所有节点均小于i,假设该节点的左子树的排列情况有m种;

  ②位于该节点右子树中的所有节点均大于i,假设该节点的右子树的排列情况有n种;

  ③综合①和②,当节点i作为当前二叉查找树的首节点时,该二叉查找树的总排列数目有m*n;

因此对于一个拥有n个节点的二叉查找树,其每一个节点均能成为某一二叉查找树的首节点,因此动态规划的表达式为:

  DP[i] = sum{ DP[j-1] * DP[i-j] },其中1=< j <=i,DP[i]表示i个节点所能组成的二叉查找树的数目;

代码: 

 public int numTrees(int n) {
        // write your code here
        if(n==0)
            return 1;
        int[] result = new int[n+1];
        result[0]=1;
        result[1]=1;
        for(int i=2;i<=n;i++){
            for(int j=1;j<=i;j++){
                int left=result[j-1];
                int right=result[i-j];
                result[i]+=left*right;
            }
        }
        return result[n];
    }

不同的二叉查找树II:

题目内容:

给出n,生成所有由1...n为节点组成的不同的二叉查找树;

样例:

给出n = 3,有5种不同形态的二叉查找树:

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

算法分析:

本题在 I 的基础上要求返回的不是组数而是具体的组合,很明显本题需要用递归的方式来解决问题,和 I 的思想基本一致,在递归的过程中找出所有的左右子树的排列组合,然后分别连接到当前节点的左子树和右子树,将结果作为当前层的二叉查找树的所有排列组合进行返回。

代码:

HashMap<String,List<TreeNode>> map = new HashMap<>();
    public List<TreeNode> findSolution(int begin, int end){
        List<TreeNode> result = new ArrayList<>();
        if(begin>end){
            result.add(null);
            return result;
        }
        if(begin==end){
            TreeNode temp = new TreeNode(begin);
            result.add(temp);
            return result;
        }
        String tag = String.valueOf(begin)+"-"+String.valueOf(end);
        if(map.get(tag)!=null){
            return map.get(tag);
        }
        for(int i=begin;i<=end;i++){
            List<TreeNode> left = findSolution(begin,i-1);
            List<TreeNode> right = findSolution(i+1,end);
            if(left.size()!=0&&right.size()!=0){
                for(TreeNode t1:left){
                    for(TreeNode t2:right){
                        TreeNode temp = new TreeNode(i);
                        temp.left = t1;
                        temp.right=t2;
                        result.add(temp);
                    }
                }
            }
            if(left.size()!=0&&right.size()==0){
                for(TreeNode t1:left){
                    TreeNode temp = new TreeNode(i);
                    temp.left = t1;
                    result.add(temp);
                }
            }
            if(left.size()==0&&right.size()!=0){
                for(TreeNode t2:right){
                    TreeNode temp = new TreeNode(i);
                    temp.right = t2;
                    result.add(temp);
                }
            }
        }
        map.put(tag,result);
        return result;
    }
    public List<TreeNode> generateTrees(int n) {
        // write your code here
        map = new HashMap<>();
        return findSolution(1,n);
        /*long startTime=System.nanoTime();
        List<TreeNode> result = findSolution(1,n);
        long endTime=System.nanoTime();
        System.out.println("运行时间为:"+(endTime-startTime));
        return result;*/
        
    }

代码中我还建立了一个map作为中间结果的记录,用来降低一些重复区间段求解的时间开销,其效果还是很明显的,当n=11时,没有map的算法时间开销为124313992ns,而有map的算法时间开销为82106610ns;但是这题我我试了下即使不用map也能够AC;

 

 

 

 

 

 

 

posted @ 2017-09-21 14:55  Revenent  阅读(666)  评论(0编辑  收藏  举报