0
点赞
收藏
分享

微信扫一扫

leetCode进阶算法题+解析(十四)

不同的二叉搜索树2

题目:给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树。

思路:其实这个题感觉又是个回溯啊。。首先选择第一个元素,然后因为是搜索二叉树,所以遵循左小右大。感觉上比较麻烦,我先去实现下看看
做完回来了,这个题怎么说呢,不是一个回溯问题,之前是我想复杂了。其实不用回溯。但是原理上是类似的。回溯是指添加,递归,然后退回上一步。而这道题好一点的是没有一个list可以专门用来存储是否添加过某元素了。而是因为数字是确定好的范围的,所以可以直接用当前值前面,当前值后面来自动判断左右树。刚说有点类似回溯的也是这点。每一个值都可能是跟节点,所以要每一种情况都走一次递归。同样其实子树的节点也都是每一个值都有可能。所以还是递归套递归。我感觉我表达能力是个大问题,可能没说明白。我直接贴代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<TreeNode> generateTrees(int n) {       
        if(n==0) return new ArrayList<TreeNode>();
        return dfs(1,n);

    }
    public  List<TreeNode> dfs(int start,int end){
        List<TreeNode> res = new ArrayList<TreeNode>();
        if(start>end){
            res.add(null);
            return res;
        }
        for(int i = start;i<=end;i++){
            List<TreeNode> left = dfs(start,i-1);
            List<TreeNode> right = dfs(i+1,end);
            for(TreeNode subLeft :left){
                for(TreeNode subRight:right){
                    TreeNode node =new TreeNode(i);
                    node.left = subLeft;
                    node.right = subRight;
                    res.add(node);
                }
            }
        } 
        return res;
    }    
}

简单说两句,重点是dfs方法中的for循环,第一层for循环,是把当前i作为根节点,然后找出左子树的所有可能性和右子树的所有可能性。这块就用到了递归,因为左子树和右子数作为单独的树,其实也是要遍历确定子树的“根节点”的。最后双层for循环可以把所有子树的可能情况都考虑到。反正情况就是这样。
刚刚看了性能排行第一的代码,就是多了点判断,我贴出来参考下:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<TreeNode> generateTrees(int n) {
        List<TreeNode> ans=new ArrayList<TreeNode>();
        if(n==0) return ans;
        return getAns(1,n);
    }

    public List<TreeNode> getAns(int start,int end){
        List<TreeNode> ans=new ArrayList<TreeNode>();
        if(start>end){
            //没有数字的时候返回n
            ans.add(null);
            return ans;
        }

        if(start==end){
            //只有一个数字的时候直接加入
            TreeNode tree=new TreeNode(start);
            ans.add(tree);
            return ans;
        }

        for(int i=start;i<=end;i++){
            //得到所有的左子树和右子树
            List<TreeNode> leftTrees=getAns(start,i-1);
            List<TreeNode> rightTrees=getAns(i+1,end);
            for(TreeNode leftTree:leftTrees){
                //分别进行组合,将其分别连接到root两侧
                for(TreeNode rightTree:rightTrees){
                    TreeNode root=new TreeNode(i);
                    root.left=leftTree;
                    root.right=rightTree;
                    ans.add(root);
                }
            }
        }
        return ans;

    }
}

然后只要理解了就简单,我反正一开始陷入了回溯的牛角尖,琢磨半天也没写出来,后来灵机一动才摸索出来了。这道题只要思路对了代码不难,然后就这样了,直接下一题。

不同的二叉搜索树

题目:给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?

思路:说真的,这两道题有毛病吧?哪怕都出了也应该这个1在前面啊,我都做出2了还有必要做1????就是上面的代码,返回值是res.size()就好了。我去粘贴提交下。好像不太多,我看题目,这个连树的结构都莫有了,,,貌似这个种类应该是有简单算法的。直接可以抛出树的结构直接数字相加,,我还是去试试代码吧。
很好,超时了。。。但是我确信我的代码没问题。。哈哈,先附上截图吧:


然后为什么超时。。。我不知道啊,还有什么更简单的算法么?我现在明白为啥2放在1前面了,都是套路啊。。哎。这道题感觉考点都变了,我再寻思寻思。
唔,我这里用的递归,问题应该就出在这里,递归超时,有一种递归的高效版:动态规划——用数组记录中间过程的递归。
我记得时间复杂度是n方 和n的区别。我去试试这道题用动归怎么解。而且我觉得这道题确实这么做是有问题的,因为 比如说三个节点能组成的方式是固定的。所以如果前面三个后面三个,只算一次就ok了,而我这段代码是要重复算的,这个是受了上一道题的影响,这里不需要列出来怎么排列,我从头开始想想怎么做吧。
好了,果然是dp问题,我直接贴代码:

class Solution {
    public int numTrees(int n) {
        if(n<=1) return n;
        //几个元素能组成多少中选择的字典
        int[] d = new int[n+1];
        //没有元素或只有一个元素只有1中可能,
        d[0] = 1;
        d[1] = 1;
        for(int i = 2;i<=n;i++){
            //以j为根节点,求出i个元素有多少种排列
            //这里只是计数,从0开始则小于i,从1开始小于等于i
            for(int j = 0;j<i;j++){        
                //因为j是从0开始,所以j直接代表下标(从1开始要j-1。同理后面是i-j)        
                d[i] += d[j]*d[i-j-1]; 
            }
        }
        return d[n];
    }
}

因为这个题我没啥思路,是一点点测试理解的,所以注释我感觉写的比较清楚了。。没啥可讲的。就是一个用数组记录中间过程的递归。如果看不懂这个或者说不太明白,建议去补习动态规划的知识。下一题了。

验证二叉搜索树

题目:给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。


思路:这道题有点送分啊。。。感觉做过类似的呢,,我有个大胆的想法。前序遍历,结果不是递增就是false。。不知道想法对不对呢。我去试试。
哈哈哈哈,我这个大胆的想法就这么实现了,有点意思啊。。。
我直接贴出第一版本的代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isValidBST(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();
        test(root,list);
        for(int i = 0;i<list.size()-1;i++){
            if(list.get(i+1)<=list.get(i)) return false;
        }
        return true;
    }
    public void test(TreeNode root,List<Integer> list){
        if(root==null) return;
        test(root.left,list);
        list.add(root.val);
        test(root.right,list);        
    }
}

讲真,leetcode中中等难度最简单的题目,没有之一。
然后这个性能不是很好,因为我本来只是想尝试下思路对不对,,估计是可以优化的,我目前想到的优化点就是不用遍历完二叉树再判断了,可以在遍历的时候就判断。新加元素小于等于前一个元素,直接false。。我去处理下。
emmmm....怎么说呢,改是改完了,运行也对了,就是性能还是2ms,一点没长进。。。我还是把代码贴上来吧,也算是个思路 :

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    List<Integer> list;
    public boolean isValidBST(TreeNode root) {
        list = new ArrayList<Integer>();
        return test(root);
    }
    public boolean test(TreeNode root){
        if(root==null) return true;        
        boolean l = test(root.left);
        if(list.size()!=0 && root.val<= list.get(list.size()-1)) return false;
        list.add(root.val);
        boolean r = test(root.right);  
        return l && r;      
    }
}

感觉凭我一己之力已经难以优化了,我直接去看性能排行第一的代码吧。
我直接贴代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isValidBST(TreeNode root) {
        return isBST(root,Long.MIN_VALUE,Long.MAX_VALUE);
    }
    public boolean isBST(TreeNode node,long min,long max){
        if(node==null){
            return true;
        }
        if(node.val<=min||node.val>=max){
            return false;
        }
        return isBST(node.left,min,node.val)&&isBST(node.right,node.val,max);
    }
}

差不多就是遍历过程中判断,只不过我用了list,人家没用。。。我感觉我是受了第一个遍历完判断的思路的影响。。。看了人家的代码恍然大悟豁然开朗,我觉得我也应该能想出来啊。。。哎
思路:然后今天的笔记就记到这里,如果稍微帮到你了记得点个喜欢点个关注!打算从今天开始正常刷题啦,反正闲着也是闲着。也祝大家工作顺顺利利!生活平平安安健健康康!

举报

相关推荐

0 条评论