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