树系列问题III——二叉树

513. 找树左下角的值
给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。

示例 1:
在这里插入图片描述

输入: root = [2,1,3]
输出: 1

示例 2:
在这里插入图片描述
输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    //层序遍历
    public int findBottomLeftValue(TreeNode root) {
        //二叉树中至少有一个节点
        if(root.left==null && root.right==null){
            return root.val;
        }
        
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        TreeNode cur=new TreeNode(Integer.MIN_VALUE);
        while(! queue.isEmpty() ){
            cur=queue.poll();
            //先加入右节点,再加入左节点
            if(cur.right!=null){
                queue.offer(cur.right);
            }
            if(cur.left!=null){
                queue.offer(cur.left);
            }
            
        }
        return cur.val;
    }
}

结果:
执行用时:1 ms, 在所有 Java 提交中击败了67.97%的用户
内存消耗:38.2 MB, 在所有 Java 提交中击败了17.23%的用户
通过测试用例:76 / 76

863. 二叉树中所有距离为 K 的结点
给定一个二叉树(具有根结点 root), 一个目标结点 target ,和一个整数值 K 。
返回到目标结点 target 距离为 K 的所有结点的值的列表。 答案可以以任何顺序返回。

示例 1:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, K = 2
输出:[7,4,1]
解释:
所求结点为与目标结点(值为 5)距离为 2 的结点,
值分别为 7,4,以及 1
在这里插入图片描述

注意,输入的 “root” 和 “target” 实际上是树上的结点。
上面的输入仅仅是对这些对象进行了序列化描述。
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    /*若以target为树的根节点,从 target 出发,使用深度优先搜索去寻找与target 距离为 k 的所有结点,即深度为 k 的所有结点
    若从target顺着父节点向上搜,但是输入的二叉树没有记录父结点,因此首先要从根结点root 出发,使用深度优先搜索遍历整棵树,同时用一个哈希表记录每个结点的父结点
    然后从target 出发,使用深度优先搜索遍历整棵树
    为避免在深度优先搜索时重复访问结点,递归时额外传入来源结点resource,在递归前比较目标结点是否与来源结点相同,不同的情况下才进行递归。
    */
    List<Integer> list=new ArrayList<>();
    Map<Integer,TreeNode> map=new HashMap<>();  //key:节点值  value:父节点
    public List<Integer> distanceK(TreeNode root, TreeNode target, int k) {
        if(root == null){
            return list;
        }
        parents(root);
        DFS(target,null,0,k);
        return list;
    }
    //使用深度优先搜索遍历整棵树,同时用一个哈希表记录每个结点的父结点
    public void parents(TreeNode Node){
        if(Node.left!=null){
            map.put(Node.left.val,Node);
            parents(Node.left);
        }
        if(Node.right!=null){
            map.put(Node.right.val,Node);
            parents(Node.right);
        }
    }
    //从target 出发,使用深度优先搜索遍历整棵树
    public void DFS(TreeNode Node,TreeNode resource,int depth,int k){
        if(Node == null){
            return;
        }
        if(depth == k){
            list.add(Node.val);
            return;
        }
        if(Node.left!=resource){
            DFS(Node.left,Node,depth+1,k);
        }
        if(Node.right!=resource){
            DFS(Node.right,Node,depth+1,k);
        }
        if(map.get(Node.val)!=resource){
            DFS(map.get(Node.val),Node,depth+1,k);
        }
    }
}

结果:
执行用时:17 ms, 在所有 Java 提交中击败了10.32%的用户
内存消耗:38.5 MB, 在所有 Java 提交中击败了57.04%的用户
通过测试用例:57 / 57

515. 在每个树行中找最大值
给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。

示例1:
输入: root = [1,3,2,5,3,null,9]
输出: [1,3,9]
解释:

 		  1
         / \
        3   2
       / \   \  
      5   3   9 

示例2:
输入: root = [1,2,3]
输出: [1,3]
解释:

 	      1
         / \
        2   3

示例3:
输入: root = [1]
输出: [1]

示例4:
输入: root = [1,null,2]
输出: [1,2]
解释:

		   1 
            \
             2 

示例5:
输入: root = []
输出: []
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    //层序遍历  比较每层中的最大值,并加入list中
    public List<Integer> largestValues(TreeNode root) {
        List<Integer> list=new ArrayList<>();
        if(root == null){
            return list;
        }
        
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        while(! queue.isEmpty()){
            int length=queue.size();
            int Maxvalue=Integer.MIN_VALUE;
            for(int i=0;i<length;i++){
                TreeNode cur=queue.poll();
                Maxvalue=Math.max(Maxvalue,cur.val);
                if(cur.left!=null){
                    queue.offer(cur.left);
                }
                if(cur.right!=null){
                    queue.offer(cur.right);
                }
            }
            list.add(Maxvalue);
        }   
        return list;
    }
}

结果:
执行用时:2 ms, 在所有 Java 提交中击败了89.36%的用户
内存消耗:38.8 MB, 在所有 Java 提交中击败了17.89%的用户
通过测试用例:78 / 78

662. 二叉树最大宽度
给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空。
每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。

示例 1:
输入:


           1
         /   \
        3     2
       / \     \  
      5   3     9 

输出: 4
解释: 最大值出现在树的第 3 层,宽度为 4 (5,3,null,9)。

示例 2:
输入:

      1
     /  
    3    
   / \       
  5   3     

输出: 2
解释: 最大值出现在树的第 3 层,宽度为 2 (5,3)。

示例 3:
输入:

      1
     / \
    3   2 
   /        
  5      

输出: 2
解释: 最大值出现在树的第 2 层,宽度为 2 (3,2)。

示例 4:
输入:

      1
     / \
    3   2
   /     \  
  5       9 
 /         \
6           7

输出: 8
解释: 最大值出现在树的第 4 层,宽度为 8 (6,null,null,null,null,null,null,7)。
注意: 答案在32位有符号整数的表示范围内。
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    //将二叉树的下标存储到数组中,根节点下标为 1,左子树结点为 2 * i,右子树下标为 2 * i+1
    //层序遍历  求出每层的宽度,然后比较每层的宽度求出最大宽度
    public int widthOfBinaryTree(TreeNode root) {
        if(root == null){
            return 0;
        }
        int Maxwidth=1;
        LinkedList<Integer> list=new LinkedList<>();
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        list.add(1);
        while(! queue.isEmpty()){
            int length=queue.size();
            for(int i=0;i<length;i++){
                TreeNode cur=queue.poll();
                Integer curindex=list.removeFirst();
                if(cur.left!=null){
                    queue.offer(cur.left);
                    list.add(2*curindex);
                }
                if(cur.right!=null){
                    queue.offer(cur.right);
                    list.add(2*curindex+1);
                }
            }
            // list 中 size 为 1 的情况下,宽度也为 1
            if(list.size()>1){
                Maxwidth=Math.max(Maxwidth,list.getLast()-list.getFirst()+1);
            }
            
        }
        return Maxwidth;
    }
}

结果:
执行用时:2 ms, 在所有 Java 提交中击败了36.43%的用户
内存消耗:37.6 MB, 在所有 Java 提交中击败了98.71%的用户
通过测试用例:112 / 112

637. 二叉树的层平均值
给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。
示例 1:

输入:

   3
   / \
  9  20
    /  \
   15   7

输出:[3, 14.5, 11]
解释:
第 0 层的平均值是 3 , 第1层是 14.5 , 第2层是 11 。因此返回 [3, 14.5, 11] 。
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    //从根节点开始搜索,每一轮遍历同一层的全部节点,计算该层的节点数以及该层的节点值之和,然后计算该层的平均值。
    //层序遍历
    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> list=new ArrayList<>();
        Queue<TreeNode> queue=new LinkedList<>();
        
        queue.offer(root);
        while(! queue.isEmpty()){
            double sum=0;
            int length=queue.size();
            for(int i=0;i<length;i++){
                TreeNode cur=queue.poll();
                sum+=cur.val;
                if(cur.left != null){
                    queue.offer(cur.left);
                }
                if(cur.right != null){
                    queue.offer(cur.right);
                }
            }
            list.add(sum/length);
        }
        return list;
    }
}

结果:
执行用时:2 ms, 在所有 Java 提交中击败了94.92%的用户
内存消耗:40.4 MB, 在所有 Java 提交中击败了29.26%的用户
通过测试用例:66 / 66

572. 另一棵树的子树
给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。
二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。

示例 1:
输入:root = [3,4,5,1,2], subRoot = [4,1,2]
输出:true

示例 2:
输入:root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2]
输出:false
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        return IsSub(root,subRoot);
    }
    //遍历root的每个节点,判断这个点的子树是否和subRoot相等。在判断相等时,需要再做一次深度优先搜索,设置两个指针,并都指向该节点与subRoot的根,然后同时移动两个指针同步遍历这两棵树,判断相应的位置是否相等
    public boolean IsSub(TreeNode root, TreeNode subRoot){
        if(root==null){
            return false;
        }
        return  judge(root,subRoot) || IsSub(root.left,subRoot)||IsSub(root.right,subRoot);
    }
    public boolean judge(TreeNode root1, TreeNode root2){
        if(root1 == null && root2 == null){
            return true;
        }
        if(root1 == null || root2 == null || root1.val != root2.val){
            return false;
        }
        return judge(root1.left,root2.left) && judge(root1.right,root2.right);
    }
}

结果:
执行用时:3 ms, 在所有 Java 提交中击败了84.26%的用户
内存消耗:38.8 MB, 在所有 Java 提交中击败了5.73%的用户
通过测试用例:182 / 182

872. 叶子相似的树
请考虑一棵二叉树上所有的叶子,这些叶子的值按从左到右的顺序排列形成一个 叶值序列 。
在这里插入图片描述
举个例子,如上图所示,给定一棵叶值序列为 (6, 7, 4, 9, 8) 的树。
如果有两棵二叉树的叶值序列是相同,那么我们就认为它们是 叶相似 的。
如果给定的两个根结点分别为 root1 和 root2 的树是叶相似的,则返回 true;否则返回 false 。

示例 1:
在这里插入图片描述
输入:root1 = [3,5,1,6,2,9,8,null,null,7,4], root2 = [3,5,1,6,7,4,2,null,null,null,null,null,null,9,8]
输出:true

示例 2:
输入:root1 = [1], root2 = [1]
输出:true

示例 3:
输入:root1 = [1], root2 = [2]
输出:false

示例 4:
输入:root1 = [1,2], root2 = [2,2]
输出:true

示例 5:
在这里插入图片描述
输入:root1 = [1,2,3], root2 = [1,3,2]
输出:false

提示:
给定的两棵树可能会有 1 到 200 个结点。
给定的两棵树上的值介于 0 到 200 之间。
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    //先对两棵树进行遍历,将所有叶子结点按顺序加入list中,然后判断两个list是否相等
    List<Integer> list1=new ArrayList<>();
    List<Integer> list2=new ArrayList<>();
    public boolean leafSimilar(TreeNode root1, TreeNode root2) {
        if(root1 == null && root2 == null){
            return true;
        }
        Helper(root1,list1);
        Helper(root2,list2);
        if(list1.size() != list2.size()){
            return false;
        }
        return list1.equals(list2);
    }
    //遍历
    public void Helper(TreeNode root,List<Integer> list){
        if(root==null){
            return;
        }
        if(root.left == null && root.right == null){
            list.add(root.val);
        }
        if(root.left != null){
            Helper(root.left,list);
        }
        if(root.right != null){
            Helper(root.right,list);
        }
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:36.1 MB, 在所有 Java 提交中击败了60.90%的用户
通过测试用例:40 / 40

563. 二叉树的坡度
给定一个二叉树,计算 整个树 的坡度 。
一个树的 节点的坡度 定义即为,该节点左子树的节点之和和右子树节点之和的 差的绝对值 。如果没有左子树的话,左子树的节点之和为 0 ;没有右子树的话也是一样。空结点的坡度是 0 。
整个树 的坡度就是其所有节点的坡度之和。

示例 1:
在这里插入图片描述

输入:root = [1,2,3]
输出:1
解释:
节点 2 的坡度:|0-0| = 0(没有子节点)
节点 3 的坡度:|0-0| = 0(没有子节点)
节点 1 的坡度:|2-3| = 1(左子树就是左子节点,所以和是 2 ;右子树就是右子节点,所以和是 3 )
坡度总和:0 + 0 + 1 = 1

示例 2:
在这里插入图片描述

输入:root = [4,2,9,3,5,null,7]
输出:15
解释:
节点 3 的坡度:|0-0| = 0(没有子节点)
节点 5 的坡度:|0-0| = 0(没有子节点)
节点 7 的坡度:|0-0| = 0(没有子节点)
节点 2 的坡度:|3-5| = 2(左子树就是左子节点,所以和是 3 ;右子树就是右子节点,所以和是 5 )
节点 9 的坡度:|0-7| = 7(没有左子树,所以和是 0 ;右子树正好是右子节点,所以和是 7 )
节点 4 的坡度:|(3+5+2)-(9+7)| = |10-16| = 6(左子树值为 3、5 和 2 ,和是 10 ;右子树值为 9 和 7 ,和是 16 )
坡度总和:0 + 0 + 0 + 2 + 7 + 6 = 15

示例 3:
在这里插入图片描述

输入:root = [21,7,14,1,1,2,2,3,3]
输出:9
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int sum=0;
    public int findTilt(TreeNode root) {
        DFS(root);
        return sum;
    }
    
    /*计算每一个节点的坡度  
    从根结点开始遍历,设当前遍历的结点为 node;
    遍历 node 的左子结点,得到左子树结点之和 sum_left;遍历node 的右子结点,得到右子树结点之和sum_right;
    将左子树结点之和与右子树结点之和的差的绝对值累加到结果变量ans;
    返回以node 作为根结点的树的结点之和 sum_left+sum_right+node.val.
    */
    public int DFS(TreeNode root){
        if(root == null){
            return 0;
        }
        int SumLeft=DFS(root.left);
        int SumRight=DFS(root.right);
        sum+=Math.abs(SumLeft-SumRight);
        return SumLeft+SumRight+root.val;
    }
    
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:38.3 MB, 在所有 Java 提交中击败了95.06%的用户
通过测试用例:77 / 77

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

示例 :
给定二叉树

      1
     / \
    2   3
   / \     
  4   5    

返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
注意:两结点之间的路径长度是以它们之间边的数目表示。
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int Max;
    public int diameterOfBinaryTree(TreeNode root) {
        if(root==null){
            return 0;
        }
        Max=1;
        Depth(root);
        return Max-1;
    }
    //节点node为起点的路径经过节点数的最大值为dnode,二叉树的直径就是所有节点dnode的最大值减1
    //对于该节点的左儿子向下遍历经过最多的节点数 L(即以左儿子为根的子树的深度) 和其右儿子向下遍历经过最多的节点数 R(即以右儿子为根的子树的深度),那么以该节点为起点的路径经过节点数的最大值即为 L+R+1。
    public int Depth(TreeNode root){
        if(root==null){
            return 0;
        }
        int Left=Depth(root.left);  // 左儿子为根的子树的深度
        int Right=Depth(root.right);
        int value=Left+Right+1;  //以该节点为起点经过的最大节点数
        Max=Math.max(Max,value);
        return Math.max(Left,Right)+1;  // 返回该节点为根的子树的深度
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:38.5 MB, 在所有 Java 提交中击败了6.54%的用户
通过测试用例:104 / 104

508. 出现次数最多的子树元素和
给你一个二叉树的根结点,请你找出出现次数最多的子树元素和。一个结点的「子树元素和」定义为以该结点为根的二叉树上所有结点的元素之和(包括结点本身)。
你需要返回出现次数最多的子树元素和。如果有多个元素出现的次数相同,返回所有出现次数最多的子树元素和(不限顺序)。

示例 1:
输入:

  5
 /  \
2   -3

返回 [2, -3, 4],所有的值均只出现一次,以任意顺序返回所有值。

示例 2:
输入:

  5
 /  \
2   -5

返回 [2],只有 2 出现两次,-5 只出现 1 次。
解析过程:
Map.getOrDefault(Object key, V defaultValue);
如果在Map中存在key,则返回key所对应的的value。
如果在Map中不存在key,则返回默认值。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    Map<Integer,Integer> map=new HashMap<>();
    int MaxFreq=0;
    public int[] findFrequentTreeSum(TreeNode root) {
        PostOrder(root);
        List<Integer> list=new ArrayList<>();
        for(int element :map.keySet()){
            //若从map中获取到的值与最大次数相同,则加入到集合list中
            if(map.get(element)==MaxFreq){
                list.add(element);
            }
        }
        int[] res=new int[list.size()];
        for(int i=0;i<list.size();i++){
            res[i]=list.get(i);
        }
        return res;
    }
    //先后序遍历,再递归相加,计算每个子树元素和和其出现次数,并加入到map中,并记录出现最多的次数
    public int PostOrder(TreeNode root){
        if(root == null){
            return 0;
        }
        int Left=PostOrder(root.left);
        int Right=PostOrder(root.right);
        int value=root.val+Left+Right;
        //map.put(num, map.getOrDefault(num, 0) + 1);   value默认从1开始,每次操作后num对应的value值加1
        map.put(value,map.getOrDefault(value,0)+1);
        MaxFreq=Math.max(MaxFreq,map.get(value));
        return value;
    }
}

结果:
执行用时:4 ms, 在所有 Java 提交中击败了81.42%的用户
内存消耗:38.9 MB, 在所有 Java 提交中击败了23.37%的用户
通过测试用例:58 / 58

671. 二叉树中第二小的节点
给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个。
更正式地说,root.val = min(root.left.val, root.right.val) 总成立。
给出这样的一个二叉树,你需要输出所有节点中的第二小的值。如果第二小的值不存在的话,输出 -1 。

示例 1:
在这里插入图片描述

输入:root = [2,2,5,null,null,5,7]
输出:5
解释:最小的值是 2 ,第二小的值是 5 。

示例 2:
在这里插入图片描述

输入:root = [2,2,2]
输出:-1
解释:最小的值是 2, 但是不存在第二小的值。
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    //对于二叉树中的任意节点 x,x 的值不大于以 x 为根的子树中所有节点的值,因此,二叉树根节点的值即为所有节点中的最小值
    //通过深度优先搜索对二叉树进行一次遍历。找出严格大于 根节点值的最小值,即为所有节点中的第二小的值
    int result;
    int Rootval;
    public int findSecondMinimumValue(TreeNode root) {
        result=-1;
        Rootval=root.val;
        DFS(root);
        return result;
    }
    public void DFS(TreeNode node){
        if(node==null){
            return;
        }
        //如果result 的值为−1 或者当前节点的值严格小于result,对 result进行更新
        //如果当前节点的值大于等于result,那么以当前节点为根的子树中所有节点的值都大于等于result,直接回溯,无需对该子树进行遍历
        if(result !=-1 && node.val>=result){
            return;
        }
        if(node.val>Rootval){
            result=node.val;
        }
        DFS(node.left);
        DFS(node.right);
    }
    
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:35.7 MB, 在所有 Java 提交中击败了46.55%的用户
通过测试用例:39 / 39

606. 根据二叉树创建字符串
你需要采用前序遍历的方式,将一个二叉树转换成一个由括号和整数组成的字符串。
空节点则用一对空括号 “()” 表示。而且你需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。

示例 1:
输入: 二叉树: [1,2,3,4]

       1
     /   \
    2     3
   /    
  4     

输出: “1(2(4))(3)”
解释: 原本将是“1(2(4)())(3())”,
在你省略所有不必要的空括号对之后,
它将是“1(2(4))(3)”。

示例 2:
输入: 二叉树: [1,2,3,null,4]

       1
     /   \
    2     3
     \  
      4 

输出: “1(2()(4))(3)”
解释: 和第一个示例相似,
除了我们不能省略第一个对括号来中断输入和输出之间的一对一映射关系。
解析过程:
递归方法:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public String tree2str(TreeNode root) {
        return PreOrder(root);
    }
    //先序遍历的方式
    public String PreOrder(TreeNode root){
        if(root == null){
            return "";
        }
        //如果当前节点没有孩子,则不需要在节点后面加上任何括号
        if(root.left==null && root.right==null){
            return root.val+"";
        }
        //如果当前节点只有左孩子
        if(root.right==null){
            return root.val+"("+PreOrder(root.left)+")";
        }else {
            return root.val+"("+PreOrder(root.left)+")("+PreOrder(root.right)+")";
        }
    }
}

结果:
执行用时:17 ms, 在所有 Java 提交中击败了16.21%的用户
内存消耗:39.9 MB, 在所有 Java 提交中击败了36.11%的用户
通过测试用例:160 / 160

迭代方法:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public String tree2str(TreeNode root) {
        return PreOrder(root);
    }
    //先序遍历的方式
    public String PreOrder(TreeNode root){
        //采用迭代的方法,设置一个栈,设置一个集合,若当前栈顶元素没有被遍历过,那么就把它加入到集合中,并开始对以它为根的子树进行前序遍历
        if(root == null){
            return "";
        }
        Stack<TreeNode> stack=new Stack<>();
        Set<TreeNode> set=new HashSet<>();
        StringBuilder str=new StringBuilder();
        stack.push(root);
        while(! stack.isEmpty()){
            root=stack.peek();
            //若当前栈顶元素已被遍历过,则移出栈,并添加“)”
            if(set.contains(root)){
                stack.pop();
                str.append(")");
            }else {
                set.add(root);
                str.append("("+root.val);
                //分情况讨论
                //1.若当前栈顶元素没有左右节点,什么都不做
                //2.若当前栈顶元素没有左节点,有右节点
                if(root.left==null && root.right!=null){
                    str.append("()");
                }
                //2.若当前栈顶元素有左右节点,先将右节点入栈,再将左节点入栈,符合先序遍历
                if(root.right!=null){
                    stack.push(root.right);
                }
                if(root.left!=null){
                    stack.push(root.left);
                }
            }
        }
        return str.substring(1,str.length()-1);
    }
}

结果:
执行用时:13 ms, 在所有 Java 提交中击败了46.82%的用户
内存消耗:38.9 MB, 在所有 Java 提交中击败了59.05%的用户
通过测试用例:160 / 160

114. 二叉树展开为链表
给你二叉树的根结点 root ,请你将它展开为一个单链表:

  • 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
  • 展开后的单链表应该与二叉树 先序遍历 顺序相同。

示例 1:
在这里插入图片描述
输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]

示例 2:
输入:root = []
输出:[]

示例 3:
输入:root = [0]
输出:[0]
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public void flatten(TreeNode root) {
        if(root == null){
            return;
        }
        //先对二叉树进行先序遍历,然后用列表存储,再展开为单链表
        List<TreeNode> node=new ArrayList<>();
        PreOrder(root,node);
        for(int i=0;i<node.size()-1;i++){
            TreeNode NewRoot=node.get(i);
            TreeNode NewRight=node.get(i+1);
            NewRoot.left=null;
            NewRoot.right=NewRight;
        }
        
    }
    //先序遍历
    public void PreOrder(TreeNode root,List<TreeNode> list){
        if(root == null){
            return ;
        }
        list.add(root);
        PreOrder(root.left,list);
        PreOrder(root.right,list);
    }
}

结果:
执行用时:1 ms, 在所有 Java 提交中击败了36.82%的用户
内存消耗:37.5 MB, 在所有 Java 提交中击败了93.55%的用户
通过测试用例:225 / 225

124. 二叉树中的最大路径和
路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。
给你一个二叉树的根节点 root ,返回其 最大路径和 。

示例 1:
在这里插入图片描述

输入:root = [1,2,3]
输出:6
解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6
示例 2:
在这里插入图片描述

输入:root = [-10,9,20,null,null,15,7]
输出:42
解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int Maxsum=Integer.MIN_VALUE;//最大路径和
    public int maxPathSum(TreeNode root) {
        //对根节点调用函数 MaxPath,即可得到每个节点的最大贡献值
        MaxPath(root);
        return Maxsum;
    }
    public int MaxPath(TreeNode node){
        //该函数计算二叉树中的一个节点的最大贡献值,在以该节点为根节点的子树中寻找以该节点为起点的一条路径,使得该路径上的节点值之和最大
        //空节点的最大贡献值等于0;非空节点的最大贡献值等于节点值与其子节点中的最大贡献值之和
        if(node == null){
            return 0;
        }
        //递归计算左右子节点的最大贡献值
        int Left=Math.max(MaxPath(node.left),0);  // 只有在最大贡献值大于 0 时,才会选取对应子节点
        int Right=Math.max(MaxPath(node.right),0);

        // 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值
        int pathsum=Left+node.val+Right;

        //更新最大路径和
        Maxsum=Math.max(Maxsum,pathsum);

        // 返回节点的最大贡献值
        return node.val+Math.max(Left,Right);
    }
}

结果:
94 / 94 个通过测试用例
执行用时: 0 ms
内存消耗: 40.4 MB

129. 求根节点到叶节点数字之和
给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。
每条从根节点到叶节点的路径都代表一个数字:

  • 例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字123 。
    计算从根节点到叶节点生成的 所有数字之和 。
    叶节点 是指没有子节点的节点。

示例 1:
在这里插入图片描述

输入:root = [1,2,3]
输出:25
解释:
从根到叶子节点路径 1->2 代表数字 12
从根到叶子节点路径 1->3 代表数字 13
因此,数字总和 = 12 + 13 = 25
示例 2:
在这里插入图片描述
输入:root = [4,9,0,5,1]
输出:1026
解释:
从根到叶子节点路径 4->9->5 代表数字 495
从根到叶子节点路径 4->9->1 代表数字 491
从根到叶子节点路径 4->0 代表数字 40
因此,数字总和 = 495 + 491 + 40 = 1026

解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    //从根节点开始,遍历每个节点,如果遇到叶子节点,则将叶子节点对应的数字加到数字之和。如果当前节点不是叶子节点,则计算其子节点对应的数字,然后对子节点递归遍历
    public int sumNumbers(TreeNode root) {
        return DFS(root,0);
    }
    public int DFS(TreeNode root,int presum){
        if(root == null){
            return 0;
        }
        int sum =presum*10+root.val;
        if(root.left == null && root.right == null){
            return sum;
        }else {
            return DFS(root.left,sum)+DFS(root.right,sum);
        }
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:35.8 MB, 在所有 Java 提交中击败了61.96%的用户
通过测试用例:108 / 108

222. 完全二叉树的节点个数
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

示例 1:
在这里插入图片描述
输入:root = [1,2,3,4,5,6]
输出:6

示例 2:
输入:root = []
输出:0

示例 3:
输入:root = [1]
输出:1

解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int count=0;//记录该树的节点个数
    public int countNodes(TreeNode root) {
        if(root == null){
            return 0;
        }
        Inorder(root);
        return count;
    }
    //中序遍历,遇到节点则count++
    public void Inorder(TreeNode root){
        if(root == null){
            return;
        }
        Inorder(root.left);
        count++;
        Inorder(root.right);
    }
   
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:40.9 MB, 在所有 Java 提交中击败了45.32%的用户
通过测试用例:18 / 18

199. 二叉树的右视图
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

示例 1:
在这里插入图片描述

输入: [1,2,3,null,5,null,4]
输出: [1,3,4]

示例 2:
输入: [1,null,3]
输出: [1,3]

示例 3:
输入: []
输出: []

提示:
二叉树的节点个数的范围是 [0,100]
-100 <= Node.val <= 100
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    List<Integer> list=new ArrayList<>();
    Queue<TreeNode> queue=new LinkedList<>();
    public List<Integer> rightSideView(TreeNode root) {
        if(root ==null){
            return list;
        }
        return Rview(root,list);
    }
    public List<Integer> Rview(TreeNode node,List<Integer> list){
        //BFS
        queue.offer(node);
        while(! queue.isEmpty()){
            int len=queue.size();
            for(int i=0;i<len;i++){
                TreeNode node1 = queue.poll();
                if(node1.left!=null){
                    queue.offer(node1.left);
                }
                if(node1.right!=null){
                    queue.offer(node1.right);
                }
                if(i==len-1){
                    //将当前层的最后一个节点放入结果列表
                    list.add(node1.val);
                }
            }
        }
        return list;
    }
}

结果:
执行用时:1 ms, 在所有 Java 提交中击败了83.23%的用户
内存消耗:37.2 MB, 在所有 Java 提交中击败了34.02%的用户
通过测试用例:215 / 215

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值