LeetCode树专题(递归)(未完成)

递归

一棵树要么是空树,要么有两个指针,每个指针指向一棵树。树是一种递归结构,很多树的问题可以使用递归来处理。

1. 树的高度

104. Maximum Depth of Binary Tree (Easy)

Leetcode / 力扣

传入的TteeNode root 结点有左结点left右结点right两个属性,是跟root一样的TreeNode。所以是递归结构。二叉链表的存储结构。

该题的递归过程分析在第3题。现在是懂了,看到再分析一下看是不是真的记住了。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        if(root==null){
            return 0;
        }
        return Math.max(maxDepth(root.left),maxDepth(root.right))+1;//返回求深度,DFS。找左儿子和右儿子的深度最大的那个加1
        
    }
}

2. 平衡树

110. Balanced Binary Tree (Easy)

Leetcode / 力扣

    3
   / \
  9  20
    /  \
   15   7

平衡树左右子树高度差都小于等于 1

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    boolean result = true;//定义一个为ture的全局变量 可是很难想到1002
    public boolean isBalanced(TreeNode root) {
        
        maxDepth(root);
        return result;
    }
    
    public int maxDepth(TreeNode root){
        if(root==null) return 0;
        if((Math.abs(maxDepth(root.left) - maxDepth(root.right))) > 1){//没有这一步的话 前面就直接返回事先定好的true。
意思是树上任意节点左右子树深度若差值大于1了的话,把全局变量在这里改成了一个false.但不影响求树的最大深度!! result
= false; } return Math.max(maxDepth(root.left),maxDepth(root.right))+1;//返回的依然是树的最大深度。但重要的是if条件判断 } }

主判断方法为isBalanced(),其中调用了maxDepth()

maxDepth()方法中,我们通过递归,计算了每个节点的左右子树的深度并进行比较,若差距大于1,则将result置为false(不平衡)

这一题比上一题这个判断

3. 两节点的最长直径

543. Diameter of Binary Tree (Easy)

Leetcode / 力扣

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。

Input:

         1
        / \
       2  3
      / \
     4   5

Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3].

都没有思路,希望第二次做时可以有思路

答案解析:

又是换一种方式用递归计算深度,然后这次需要两边最大的深度加起来就是最长路径

递归出口很重要

这一题总结下两题的求深度(深度优先搜索):

 

 这个第一题的递归过程是1的左儿子2先进入这一行里的递归方法,执行方法回到第一行,看2是否为空,不为空往下走,找到左儿子4继续这个方法,又从第一行从上往下,先看4是否为空,不为空往下走,找4的左儿子执行方法,为空了此时,retrun 0出了这一行递归,L=0。继续往下一行代码走。

又进入递归方法,此时rt还是4,找4的右儿子(后1/2)从递归方法第一行执行,为空了此时,retrun 0出了这一行递归,R=0。

4的前1/2和后1/2都完成了。。。。

然后往下走 return max(0,0)+1  =1 返回的是4的深度

然后得返回上一层到2的右儿子(后1/2...)   好突然

找到右儿子5,又进入递归方法第一行,由于是空 return 0 。找5的右儿子,又进入递归方法第一行,由于是空return 0...

5的完成,往下走return 1返回的是5的深度

2也前后都完成了,往下走return max(1,1)+1 =2 返回的是2的深度  好突然

就是每一个结点得左右儿子完成,return出一个数,才返回上一层

返回到1的右儿子3执行递归方法,左儿子空=0,右儿子空=0,return max(0,0)+1 =1是3的深度

然后1的深度就是max(2,1)+1=3

总结过程就是遇到递归方法参数进去,返回到递归方法第一行,一直执行到递归出口,此时的最底层执行下一个语句,若又是递归则又到第一行往下执行,若遇到递归出口,则可以该层完成后1/2,返回上一层完成递归后半段1/2整体完成直到递归出口。(走一遍就明白了)。关于什么时候用,应该是遇到传入的数据结构是一种递归结构去实现某个方法时,(链表好像也可以? 或者像阶乘这种方法

然后我们通过左右遍历返回有可能就认识这个3就是答案,这在上图是对的,但是在下图就不对了。

如果是下图,按上面从1出发的思路找到3条,就不对了。最多应该是不经过1的4条。

 注意可能并不穿过根节点,这里主方法中的Max=depth(root.left)+depth(root.right)是只取经过主节点的左加右长度!!!

改掉这个代码,不用Max只等于root的左加右,改成Max--任意节点的左加右存住最大的那个返回Max给主方法。

这个改的过程有点难理解,但是结合刚刚递归的过程,加上如上图这种情况分析递归,当从5后1/2的完成返回到2这里的时候,2的后1/2完成,此时L是4返回的2,R是5返回的2,L+R=4,Max存住了。就算再返回到1,1的后1/2返回的R=0,L为2返回的3,L+R =3 其实小于2的左右相为4.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int max=0;
    public int diameterOfBinaryTree(TreeNode root) {
        deepth(root);
        return max;
    }
public int deepth(TreeNode root){ if(root==null){ return 0; } int L,R; L=deepth(root.left);//左子的深度 R=deepth(root.right);//右子的深度 max=Math.max(L+R,max);//某个节点为头结点时,此时更新的最大的左子加右子的深度,是最长的路径 return Math.max(L,R)+1;//这是节点的深度的计算。但是我们重要的是return的max。上一题也是 } }

4. 翻转树

226. Invert Binary Tree (Easy)

Leetcode / 力扣

思路:

    public TreeNode invertTree(TreeNode root) {
        if(root==null){
            return null;
        }
        invertTree(root.left);
        invertTree(root.right);
        root.left=root.right;//错了。。。。。
        return root;
    }

思路是对的,交换语句写错了!!这是赋值。。不是交换。。。

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root==null){
            return null;
        }
        invertTree(root.left);
        invertTree(root.right);

        TreeNode temp;
        temp=root.right;
        root.right=root.left;
        root.left=temp;
        
        return root;
    }
}

5. 归并两棵树

617. Merge Two Binary Trees (Easy)

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

Leetcode / 力扣

Input:
       Tree 1                     Tree 2
          1                         2
         / \                       / \
        3   2                     1   3
       /                           \   \
      5                             4   7

Output:
         3
        / \
       4   5
      / \   \
     5   4   7

思路是列出三种情况,然后递归相加。怎么写val想加呢?

错误之处:

1.应该是先相加,再递归。(前序遍历就是这样,先打印值,再判断有没有左,有左就进入递归,再判断有没有右,有右再进入递归。)

2.相加的结果直接用构造方法new出结果结点传入值作为参数即可。

3.左右递归的结果分别要做为结果结点的左子树和右子树。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }//构造方法new一个就是
 * }
 */
class Solution {
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
        if(t1==null && t2==null) return null;
        if(t1==null) return t2;
        if(t2==null) return t1;

        TreeNode root = new TreeNode(t1.val+t2.val);
        root.left= mergeTrees(t1.left,t2.left);
        root.right=mergeTrees(t1.right,t2.right);
        return root;

    }
}

6. 判断路径和是否等于一个数

Leetcdoe : 112. Path Sum (Easy)

Leetcode / 力扣

Given the below binary tree and sum = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \      \
        7    2      1

return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.

路径和定义为从 root 到 leaf 的所有节点的和。

emmm没有想法。。

答案:

最直接的方法就是利用递归,遍历整棵树:如果当前节点不是叶子,对它的所有孩子节点,递归调用 hasPathSum 函数,其中 sum 值减去当前节点的权值如果当前节点是叶子,检查 sum 值是否为 0,也就是是否找到了给定的目标和。

(和是摊到每个节点的,所以一路减下来到叶子结点的值就是叶子结点的值)

class Solution {
  public boolean hasPathSum(TreeNode root, int sum) {
    if (root == null)
      return false;

    sum -= root.val;
    if ((root.left == null) && (root.right == null))//一般是判断节点为空 但这里需要判断是否是叶子 没到下一步
      return (sum == 0);//到达了叶子,判断是否达到了目标值。=0则说明加上这个叶子值达到了目标
return hasPathSum(root.left, sum) || hasPathSum(root.right, sum);//可以分开看 只要左右有一个达到为==0为ture就行 } }

这题有点难想到。但愿下次会做了。。emmmm1003还是不会

7. 统计路径和等于一个数的路径数量

437. Path Sum III (Easy)

Leetcode / 力扣

root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8

      10
     /  \
    5   -3
   / \    \
  3   2   11
 / \   \
3  -2   1

Return 3. The paths that sum to 8 are:

1.  5 -> 3
2.  5 -> 2 -> 1
3. -3 -> 11

路径不一定以 root 开头,也不一定以 leaf 结尾,但是必须连续。

思路是第三题也不是必须经过root,在中间加一个存住满足的树。(应该是想到让其他节点为头结点时

class Solution {
    public int pathSum(TreeNode root, int sum) {
        if(root == null){
            return 0;
        }
        int count=0;
        sum-=root.val;
        int left = pathSum(root.left,sum);
        int right = pathSum(root.right,sum-left);//错的!!
        if(left+right == sum){
            count++;
        }
        return count;

    }
}

错了。。int right明显就不对。

答案:

题目要求 路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点) 。这就要求我们只需要去求三部分即可:

以当前节点作为头结点的等于sum路径数量
以当前节点的左孩子作为头结点的等于sum路径数量
以当前节点的右孩子作为头结点的等于sum路径数量
将这三部分之和作为最后结果即可。1003:其实也是递归

最后的问题是:我们应该如何去求以当前节点作为头结点的路径的数量?这里依旧是按照树的遍历方式模板,每到一个节点让sum-root.val,并判断sum是否为0,如果为零的话,则找到满足条件的一条路径。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int pathSum(TreeNode root, int sum) {
        if(root == null) return 0;
        //当前节点开始的所有满足条件的路径总数
        int result = countPath(root,sum);
        //当前节点左子节点开始的满足条件路径总数
        int a = pathSum(root.left,sum);
        //当前节点右子节点开始的满足条件路径总数
        int b = pathSum(root.right,sum);
        return result+a+b;
    }
    public int countPath(TreeNode root,int sum){//这个方法是传入作为头结点时,计算等于sum的路径数
        if(root == null)  return 0;
        sum = sum - root.val;
        int result = sum == 0 ? 1:0;//result计数
        //当前节点开始的所有满足条件的路径总数分为左,右两条路径
        int left =count(root.left,sum);
        int right = count(root.right,sum);
        return result+left+right;
    }
}

不知道什么时候需要分开另外写方法。。第三题也另外写了方法。。

8. 子树

572. Subtree of Another Tree (Easy)

给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。

Leetcode / 力扣

Given tree s:
     3
    / \
   4   5
  / \
 1   2

Given tree t:
   4
  / \
 1   2

Return true, because t has the same structure and node values with a subtree of s.

Given tree s:

     3
    / \
   4   5
  / \
 1   2
    /
   0

Given tree t:
   4
  / \
 1   2

Return false.

 思路:可以像上一题一样,

class Solution {
    public boolean isSubtree(TreeNode s, TreeNode t) {
        if(s==null||t==null){
            return false;
        }
        return (sub(s,t)||isSubtree(s.left,t)||isSubtree(s.right,t));
    }

    public boolean sub(TreeNode s,TreeNode t){
        if(s==null||t==null){
            return false;
        }
        if(s==t){//错了 这是内存地址判断了。。。应该用值判断 但思路是对的 判断头或左子或右子 有一个是就是
            return true;
        }
        sub(s.left,t);
        sub(s.right,t);
        return true;
    }
}

错了。。。。。。。啥都return true了。。。

 最下面一行改成

        return sub(s.left,t)||sub(s.right,t);

又错了。。。变成啥都return false了。。。

看答案,首先第一行就错了,应该是

        if (t == null) return true;   // t 为 null 一定都是 true
        if (s == null) return false;  // 这里 t 一定不为 null, 只要 s 为 null,肯定是 false

然后看s和t是否一样不能用s==t....要用值判断。。。。我这么这么二居然直接用== 这是存的地址。。。

答案:

class Solution {
    public boolean isSubtree(TreeNode s, TreeNode t) {//判断是否为子树
        if (t == null) return true;   // t 为 null 一定都是 true
        if (s == null) return false;  // 这里 t 一定不为 null, 只要 s 为 null,肯定是 false
        return isSubtree(s.left, t) || isSubtree(s.right, t) || isSameTree(s,t);//s判是否值相同,s的左右再判断是否是子树
    }

    /**
     * 判断两棵树是否相同
     */
    public boolean isSameTree(TreeNode s, TreeNode t){
        if (s == null && t == null) return true;//有可能一开始就是两个null是same的,也有可能递归到了最底层它们的值都是相等的 
        if (s == null || t == null) return false;//注意这里是蟠相同不是判子树了所以不能为null了
        if (s.val != t.val) return false;//头结点处的值是否相同 不相等直接false
        return isSameTree(s.left, t.left) && isSameTree(s.right, t.right);//若相同,判断左子和右子递归该方法来判断值是否相同
    }
}

作者:kelly2018
链接:https://leetcode-cn.com/problems/subtree-of-another-tree/solution/java-di-gui-ban-by-kelly2018/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

9. 树的对称

101. Symmetric Tree (Easy)

给定一个二叉树,检查它是否是镜像对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

Leetcode / 力扣

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

emmm这每层情况都有变化啊,不能再用上一题和上上题的思路了。

就得从1开始做判断吧?我的思路:把从第二层开始另起方法。。

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null){
            return true;
        }
        
        if(root.left.val==root.right.val){
            return (yes(root.left,root.rig
        }
        return false;
    }

    public boolean yes(TreeNode left,TreeNode right){
        if(left==null&&right==null){
            return true;
        }
        if(left == null||right==null){
            return false;
        }

        if(left.left.val!=right.right.val||left.right.val!=right.left.val){//错了 空指针异常了。。
            return false;
        }
        return (yes(left.left,left.right)&&yes(right.left,right.right));

    }
    
}

答案:

 广度周游用队列??没有见过。。

也是从子树第二层开始看。然后我是在主方法中判断他们的两个根结点的值是否相同。相同才可进入下面方法。这样造成了空指针异常且多此一举了。因为其实后面用left.left和right.right再传入后也要判断值是否相等,我没看出这第二层本身就开始递归了。

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null){
            return true;
        } 
            return (yes(root.left,root.right));//改了这里直接传入
    }

    public boolean yes(TreeNode left,TreeNode right){
        if(left==null&&right==null){
            return true;
        }
        if(left == null||right==null){
            return false;
        }

        if(left.val!=right.val){//改了这里判断传入的两个的值 
            return false;
        }
        return (yes(left.left,right.right)&&yes(left.right,right.left));//改了这里 递归在第二层的子树们开始
    } 
}

方法2:用队列

 

加入根节点两遍,进入while循环,取出看这俩值相等的话,再加入一个根节点的左子,一个根节点的右子,继续循环。

10. 最小路径

111. Minimum Depth of Binary Tree (Easy)

Leetcode / 力扣

树的根节点到叶子节点的最小路径长度

 这不就是第一题一模一样的思路吗

 

 错了。。当只有左子没有右子时,他去选择右子返回0了

 不知道咋改。。。

答案:

因为题1是只需要有一条最大就行,不用管子树=null的情况。这里需要考虑一下各种情况。

又改错了。。。

 而且这样的顺序错了

答案就得这样顺序:

class Solution {
    public int minDepth(TreeNode root) {
        //1.若当前节点为空,直接返回0
          if(root==null) return 0;
        //2.左孩子和有孩子都为空的情况,说明到达了叶子节点,直接返回1即可
          if(root.left==null && root.right==null) return 1;
        //3.如果左孩子和由孩子其中一个为空,说明ldepth1和rdepth有一个必然为0,所以可以返回ldepth + rdepth + 1;
          int ldepth = minDepth(root.left);
          int rdepth = minDepth(root.right);
         if(root.left==null||root.right==null) return ldepth+rdepth+1;
        //4.最后一种情况,也就是左右孩子都不为空,返回最小深度+1即可
        return Math.min(ldepth,rdepth)+1;
    }
}

11. 统计左叶子节点的和

404. Sum of Left Leaves (Easy)

计算给定二叉树的所有左叶子之和。

Leetcode / 力扣

    3
   / \
  9  20
    /  \
   15   7

There are two left leaves in the binary tree, with values 9 and 15 respectively. Return 24.

 emmm应该要另写方法得出叶子,看答案吧。。

答案这个有点像第二题第三题那样的结构,直接传入root调用方法在方法里判断root(必须从root出发的好像都这样,有一个全局变量用来返回),这里多传入一个false。

class Solution {
    int sum = 0;

    public int sumOfLeftLeaves(TreeNode root) {
        return getSum(root, false);
    }

    private int getSum(TreeNode root, boolean isLeft){//传入结点左子值会通过递归全部加到sum里面
        if(root != null){
            if(isLeft && root.left == null && root.right == null){//满足isLeaf=true,且没有左儿子没有右儿子
                sum += root.val;//左叶子和
            }else{
                getSum(root.left, true);//是左边才设为ture
                getSum(root.right, false);//如果是求所有叶子节点相加,就把这里为true了
            }
        }
      return sum; } } 作者:Omooo 链接:https:
//leetcode-cn.com/problems/sum-of-left-leaves/solution/chao-ji-rong-yi-li-jie-qi-shi-he-qiu-quan-lu-jing-/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

12. 相同节点值的最大路径长度

687. Longest Univalue Path (Easy)

给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。

Leetcode / 力扣

             1
            / \
           4   5
          / \   \
         4   4   5

Output : 2

emmm有点没看懂题目,最长的路径,节点的值有相同的?和哪个相同?

明白了,路径长度为值。就是找最长的路经。

 第3题是最大直径,这里是最大路经。

答案和第三题还有点像,但这里要用到值。

跟着第三题的思路只写出求树中以某个结点为根的最大值。。。

class Solution {
    int total = 0;
    public int longestUnivaluePath(TreeNode root) {
        maxLength(root);
        return total;
    }
    public int maxLength (TreeNode root){
        if(root==null){
            return 0;
        }
        int L,R;
        
        L=maxLength(root.left);
        R=maxLength(root.right);
        total = Math.max(L+R+root.val,total);
        return (Math.max(L,R))+root.val;
    }
}

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    private int path = 0;

    public int longestUnivaluePath(TreeNode root) {
          dfs(root);
          return path;
    }

    private int dfs(TreeNode root){//DFS深度优先搜索 返回值为路径
          if (root == null) return 0;
          int left = dfs(root.left);
          int right = dfs(root.right);
          int leftPath = root.left != null && root.left.val == root.val ? left + 1 : 0;//??
          int rightPath = root.right != null && root.right.val == root.val ? right + 1 : 0;//???
          path = Math.max(path, leftPath + rightPath);
          return Math.max(leftPath, rightPath);
    }
}

可能还是没理解题目。。。看不懂代码。。。

先跳了。。。

明白了 他是找一条值全部相同的最长路径,不一定经过当前的根节点

class Solution {
    int ans;
    public int longestUnivaluePath(TreeNode root) {
        ans = 0;
        longestPath(root);
        return ans;
    }
    //递归函数功能:搜寻以node为起点的最长同值路径:要么是以node为起点的左子树,要么是以node为起点的右子树
    public int longestPath(TreeNode node) {
        if (node == null) return 0;
        int maxLorRres=0;
        int left = longestPath(node.left); //node左子树的最长同值路径
        int right = longestPath(node.right);//node右子树的最长同值路径
        //这种情况对于寻找最长同值路径长有帮助,对于搜索以root为路径起始点的最长路径没有帮助
        if (node.left != null && node.left.val == node.val&&node.right != null && node.right.val == node.val) {
            ans=Math.max(ans, left + right+2);
        }
        //从左右子树中选择最长的同值路径
        if(node.left!=null&&node.left.val == node.val){
            maxLorRres=left+1;
        }
        if(node.right!=null&&node.right.val==node.val){
            maxLorRres=Math.max(maxLorRres,right+1);
        }
        //从ans与maxLorRres中更新最大值
        ans=Math.max(ans,maxLorRres);
        return maxLorRres; //所以你能知道为什么返回这个值了吗?

回答一下为什么最后返回的maxLorRres而不是ans,首先这个递归函数的返回值是Int,肯定用来计数的。
ans其实是作者利用这个递归函数在函数中所求的值,与递归条件,返回值什么无关,那么所有的递归出口都将在int maxLorRres=0;这行代码之后,
则 maxLorRres则是已经被申明了,后续的if判断条件则是在统计这个 maxLorRres的值,而递归的形式则是递归左子树或者右子树
} } 作者:Chuancey 链接:https://leetcode-cn.com/problems/longest-univalue-path/solution/guan-yu-di-gui-si-lu-de-chao-xiang-xi-ge-ren-jian-/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

13. 间隔遍历

337. House Robber III (Medium)

Leetcode / 力扣

     3
    / \
   2   3
    \   \
     3   1
Maximum amount of money the thief can rob = 3 + 3 + 1 = 7.

 直接看答案:

总体思路为:分别计算单层数据的和与双层数据的和,返回较大的那个值。其实就是简单递归的应用啊。。没反应过来递归结构。。

单层数据之和的计算:当前节点的值,加上左子节点的两个子节点的和,再加上右子节点的两个子节点的和。

双层数据之和的计算:当前节点的两个子节点的值之和。

class Solution {
    public int rob(TreeNode root) {
        //若当前节点为null,直接返回0
        if(root==null) return 0;
        //计算val1(表示单层数据的和)
        int val1=root.val;//重要!!!!
        //如果当前节点的左节点不为null,那就将当前节点的值加上左节点的两个子节点的值
        if(root.left!=null) val1+=rob(root.left.left)+rob(root.left.right);
        //如果当前节点的右节点不为null,那就将当前节点的值加上右节点的两个子节点的值
        if(root.right!=null) val1+=rob(root.right.left)+rob(root.right.right);
        //计算val2(表示双层数据的和)只用看它的左右节点
        int val2=rob(root.left)+rob(root.right);
        //返回val1与val2中更大的那个值
        return Math.max(val1,val2);
    }
}

14. 找出二叉树中第二小的节点

671. Second Minimum Node In a Binary Tree (Easy)

给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么这个节点的值不大于它的子节点的值。 

给出这样的一个二叉树,你需要输出所有节点中的第二小的值。如果第二小的值不存在的话,输出 -1 。

Leetcode / 力扣

Input:
   2
  / \
 2   5
    / \
    5  7

Output: 5

一个节点要么具有 0 个或 2 个子节点,如果有子节点,那么根节点是最小的节点。

我的思路用起了优先队列。。但是不知道怎么遍历取到有左右子的结点。。。

答案:

原来人家本来就是排好序的。。从小到大。。。

思路分析:

首先根据题目描述,根结点值一定小于或者等于子结点值,这就是一个最小堆,根结点值就是整个树中的最小值,所以要找的第二小值,就是除去根结点值的最小值,为了方便使用rootValue表示这棵树根节点的值。
树的题一般考虑递归。考虑到题目所求,递归函数的返回值应该定义为以节点x为根的树中非rootValue的最小值。由于一直需要信息rootValue,所以递归函数的参数除了结点还有rootValue。(这就有点想isLeft的那个题了)
对于以TreeNode x为根的结点,
如果其值不为rootValue,那么它的值就是以它为根的树中非根结点值的最小值
如果它的值为rootValue,而且其已经是叶子结点x.left == null,在以它为根的树中没有找到非根结点值的最小值,返回失败标志-1。
如果它的值为rootValue,但它还有左右子树,就去找左右子树的非根结点值的最小值,分别为leftMin, rightMin
如果leftMin == -1,左子树查找失败,返回右子树的查找结果(也可能是失败,但失败也是返回-1,也还是rightMin)。
同理rightMin == -1,返回左子树的查找结果
如果左右都没有查找失败,都找到了非根结点值的局部最小值,就要选择其中更小的一个返回。
时间复杂度为O(n)O(n), 空间复杂度与树高成正比。

class Solution {
   public int findSecondMinimumValue(TreeNode root) {
        if(root == null)
            return -1;
        return find(root, root.val);
    }

    /**
     * 按照题目描述,这个最上面的元素就是最小的元素。用rootValue表示最小值
     * 找到和rootValue值不相同的最小值,与rootValue不相同的最小值其实就是第二小的值。
     */
    private int find(TreeNode x, int rootValue){
        if(x.val != rootValue) // 如果当前结点不等于根结点,那么值为以x为根的最小的非rootValue的值。
            return x.val;//递归终点1
// 这之下都是 当前结点值为根结点值的情况 if(x.left == null) // 如果它的值为rootValue,而且其已经是叶子结点x.left == null,在以它为根的树中没有找到非根结点值的最小值,返回失败标志-1。 return -1;//递归终点2
int leftMin = find(x.left, rootValue);//左右两边分别找最小值,因为递归终点1或2出来的一定是不等于root值的第2小的val或者-1 int rightMin =
find(x.right, rootValue);//传入的还是最小值,根结点的值!
if(leftMin == -1) return rightMin; if(rightMin == -1) return leftMin;
return Math.min(leftMin, rightMin);//左右两边比较各自的最小值 } } }

作者:ustcyyw
链接:https://leetcode-cn.com/problems/second-minimum-node-in-a-binary-tree/solution/671java-0msdi-gui-hen-jian-ji-e-by-ustcyyw/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

哎呀好难啊不会

从230来的,想到了671所以理解了理解了。。

 

posted @ 2020-06-02 20:03  xinxinpang  阅读(182)  评论(0编辑  收藏  举报