Leetcode:不同的二叉搜索树 II

题目描述:给定一个整数 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]
]
解释:
以上的输出对应以下 5 种不同结构的二叉搜索树:

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

先说一下二叉搜索树的特点
对于任意节点都满足:所有左子树的节点值小于根的值,所有右子树的节点值都大于根的值。

解题思路
这个题一开始我的一个想法是使用深度优先搜索dfs(Depth First Search缩写)。
但开始的具体解题思路并不好实现于解这道题,因为我的做法是:利用dfs找出n的全排列集合,比如,3,就有123,132,213,231,312,321,然后是那个用这个序列进行搜索树的生成。
下面是dfs找n全排列的过程:

    public static void findSolution(int n){
        int book[][]= new int[n+1][n+1]; //标记数组
        List<Integer> a = new ArrayList<>();  //记录一次排列顺序
        int cur = 1; //表示当前位置
        boolean hasNext = true; //是否还有下一个
        while(true){ 
            for(int k = 1 ; k<= n ;k++){   
                hasNext = false;
                if(cur == n+1){
                    hasNext = false;
                    //这里可以加入:生成树并加入list的操作
                    for(int i = 0 ; i< a.size(); i++){
                        //打印找到的一个排列
                        System.out.print(" "+a.get(i));
                    }
                    System.out.println(" ");
                    break;
                }
                System.out.println("当前位置:"+cur+",k="+k+",book[cur][k]="+book[cur][k]+",a.contains(k)="+a.contains(k));
                if(book[cur][k] !=1 && !a.contains(k)){
                    a.add(k);
                    book[cur][k] = 1;
                    cur++;
                    hasNext = true;
                    break;
                }
            }
            if(!hasNext){
                cur--;
                if(cur == 0)
                    return;
                a.remove(cur-1);
                System.out.println("本次移除元素:"+cur);
                if(a.isEmpty()){
                    System.out.println("length="+book.length);
                    for(int i = 2 ; i< book.length; i++){
                        book[i] = new int[n+1];
                    }
                }
            }
        }
    }

但这样做会有一个很严重的问题:比如3的全排列有6种可能,但生成的二叉搜索树只有5种,说明有重复。所以还需要去重复。

上面这种做法整个过程并不是十分清楚,当然是在有了一定比较你才会觉得。也就是下面这种做法,思路十分的清晰。

建议:在分析的过程中建议在纸上画一画。

思路:比如3,那我们就可以分别让1,2,3作为根节点,并以这个根节点作为分界线,在它左边的数就是它的左子树对应的节点集;在它右边的就是它的右子树对应的节点集。就会产生如下情况:
①root.val = 1:左子树集{},右子树集{2,3}
②root.val = 2:左子树集{1},右子树集{3}
③root.val = 3:左子树集{1,2},右子树集{}
然后你会观察到,为null的和只有一个元素的其实已经可以和root节点建立联系了,但是超过2个元素的集合还是需要去选择的。

所以我们可以把2个以上元素的再进行类似这样的划分:
把①中右子树集{2,3}:我们可以
让2为root,那么root的左子树集{},右子树集{3}
让3为root,那么root的左子树集{2},右子树{}
把③中左子树集{1,2}我们可以
让1为root,那么root的左子树集{},右子树集{2}
让2为root,那么root的左子树集{1},右子树集{}

这个时候所有的集合都被化为{}或者{一个元素}的情况了。
然后我们还可以吧{一个元素}这种集合也分解掉,这样就可以让结束程序的地方更加简洁,{一个元素}这种情况很好分解,直接让它当root然后左子树集{},右子树集也为{}。这样结束的条件就变成{}这种情况了。

下面看代码吧:

 public List<TreeNode> generateTrees(int start,int end) {
         List<TreeNode> list = new ArrayList<>();
         //当start>end就表示子集没有元素了
         if(start>end){
             list.add(null);
         }
         //i从start到end就是分别让它们去做root,
         //这里的root不是分得是最根部
         //是指相对于left和right的那个root
         for(int i = start; i<= end ; i++){
             List<TreeNode> lefts = generateTrees(start,i-1);
             List<TreeNode> rights = generateTrees(i+1,end);
             for(TreeNode left : lefts){
                 for(TreeNode right : rights){
                	 //从下面这句就可以看出上面三段注释
                     TreeNode root = new TreeNode(i);
                     root.left = left;
                     root.right = right;
                     list.add(root);
                 }
             }
         }
         return list;
     }

这个写法真的是很简洁呢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值