LeetCode_二叉树刷题笔记2(java)

473 阅读7分钟

这个系列主要是记录我刷题的过程。重点是每一类型题解法的循序渐进,按着这个顺序基本每一题都能做出来。而不是某一题的解法,所以适合打算大量刷题的人参考。二叉树相关的刷题顺序是参考 代码随想录,感兴趣的可以到该公众号上下载对应pdf。

LeetCode_二叉树刷题笔记4(java)

LeetCode_二叉树刷题笔记3(Java)

LeetCode_二叉树刷题笔记2(java)

LeetCode_二叉树刷题笔记1(java)

下面的12道题需要些二叉树遍历的基础,题目不难。

image-20210219230547566

226. 翻转二叉树

难度简单

翻转一棵二叉树。

示例:

输入:

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

输出:

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

备注: 这个问题是受到 Max Howell 原问题 启发的 :

谷歌:我们90%的工程师使用您编写的软件(Homebrew),但是您却无法在面试时在白板上写出翻转二叉树这道题,这太糟糕了。

思路

这可是一道难倒了 Max Howell的算法!!!!

配合笔记一,其实不难想到,使用遍历并配合swap就能解出。遍历其实还是递归写起来更舒适,下面使用前序遍历。代码很简单:

/**
 * 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 TreeNode invertTree(TreeNode root) {
      	if(root==null)return root;
      	swapRootChildren(root);
      	invertTree(root.left);
      	invertTree(root.right);
				return root;
    }
  	//交换当前结点下的两个子节点的位置
  	private void swapRootChildren(TreeNode root){
      TreeNode temp = root.left;
      root.left = root.right;
      root.right = temp;
    }
}

看了下性能还行:

image-20210219121625654

101. 对称二叉树

难度简单1240

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

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

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

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

    1
   / \
  2   2
   \   \
   3    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 boolean isSymmetric(TreeNode root) {
			return check(root,root);
    }
  	//从root开始,分两个方向挨个向下遍历并比较
  	public boolean check(TreeNode l,TreeNode r){
      //需要主要null节点也是需要对称,且左右节点都为null,也可以退出递归了
      if(l==null&&r==null) return true;
      if(l==null||r==null) return false;
      //注意这里递归的元素 需要对称(镜像) l.right--r.left  l.left--r.right
      return l.val==r.val&&check(l.left,r.right)&&check(l.right,r.left);
    }
}

image-20210219131043906

104. 二叉树的最大深度

难度简单792

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例: 给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 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 {
    private int maxD;
    public int maxDepth(TreeNode root) {
        if(root==null) return 0;
        preoder(root,0);
        return maxD;
    }

    private void preoder(TreeNode root,int deep){
        if(root==null){
            maxD=Math.max(deep,maxD);
            return;
        }
        deep++;
        preoder(root.left,deep);
        preoder(root.right,deep);
      	//这里其实是回溯了
        deep--;
    }
}

image-20210219132604656

其实这道题使用 **层序遍历,**更符合正常逻辑,因为层序遍历就是一层层往下走,走到最后一层肯定就是最大深度了:

/**
 * 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 maxDepth(TreeNode root) {
        Queue<TreeNode> queue = new ArrayDeque();
        if(root!=null){
            queue.add(root);
        }
        int depth =0;
        while(!queue.isEmpty()){
            int n =queue.size();
            //其实走到whilie里面一次 正好是增加一层
            depth++;
            for(int i = 0 ;i<n; i++){
                TreeNode node = queue.poll();
                if(node.left!=null){
                    queue.add(node.left);
                }
                if(node.right!=null){
                    queue.add(node.right);
                }
            }
        }
        return depth;

    }
}

559. N 叉树的最大深度

难度简单148

给定一个 N 叉树,找到其最大深度。

最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。

N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔(请参见示例)。

示例 1:

img

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

示例 2:

img

输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
输出:5

提示:

  • 树的深度不会超过 1000
  • 树的节点数目位于 [0, 104] 之间。

思路

这一题用104. 二叉树的最大深度的思路就行,不过需要注意N叉树的叶子结点的判断条件是children为空。还用本题用层序遍历的思路会超时:

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
    private int maxD;
    public int maxDepth(Node root) {
        if(root==null) return 0;
        preoder(root,0);
        return maxD;
    }

    private void preoder(Node root,int deep){
        deep++;
      	//注意叶子结点的判断条件
        if(root.children.isEmpty()){
            maxD=Math.max(deep,maxD);
            return;
        }
        for(Node child: root.children){
            preoder(child,deep);
        }
        deep--;
       
    }
}

111. 二叉树的最小深度

难度简单453

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

**说明:**叶子节点是指没有子节点的节点。

示例 1:

img

输入:root = [3,9,20,null,null,15,7]
输出:2

示例 2:

输入:root = [2,null,3,null,4,null,5,null,6]
输出:5

提示:

  • 树中节点数的范围在 [0, 105]
  • -1000 <= Node.val <= 1000

思路

这题如果直接使用104. 二叉树的最大深度](leetcode-cn.com/problems/ma…** 。什么是叶子结点呢?没有子节点的叫作叶子节点,即left和right都为null

image-20210219143905568
/**
 * 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 {

    private int minD=Integer.MAX_VALUE;
    public int minDepth(TreeNode root) {
        if(root==null) return 0;
        preoder(root,0);
        return minD;
    }

    private void preoder(TreeNode root,int deep){
        if(root==null){
          //这不能认为是一条路径
            return;
        }
        deep++;
      	//这才是到了叶子节点
        if(root.left==null&&root.right==null){
            minD=Math.min(deep,minD);
            return;
        }
        preoder(root.left,deep);
        preoder(root.right,deep);
        deep--;
    }
}

222. 完全二叉树的节点个数

难度中等434

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。

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

示例 1:

img

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

示例 2:

输入:root = []
输出:0

示例 3:

输入:root = [1]
输出:1

提示:

  • 树中节点的数目范围是[0, 5 * 104]
  • 0 <= Node.val <= 5 * 104
  • 题目数据保证输入的树是 完全二叉树

思路

这道题可以按普通二叉树来解:

/**
 * 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 countNodes(TreeNode root) {
        if(root==null) return 0;
        return 1+ countNodes(root.left)+countNodes(root.right);
    }
}

这道题还可以按照 完全二叉树的特性来解。完全二叉树中肯定有一部分是 满二叉树组成。而满二叉树的节点个数只需要知道树深度(H)即可:2<<H-1(2的H次方):

image-20210219155400052

/**
 * 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 countNodes(TreeNode root) {
       if(root==null) return 0;

       TreeNode left =root.left;
       TreeNode right =root.right;

       int leftHeight =0;
       while(left!=null){
           leftHeight++;
           left=left.left;
       }
       int rightHeight = 0;
       while(right!=null){
           rightHeight++;
           right=right.right;
       }
      //左右高度相同的是满二叉树
       if(leftHeight==rightHeight){
           return (2<<leftHeight)-1;
       }
      //不是满二叉树的部分还是上面那种一个一个加的方法
        return countNodes(root.left)+countNodes(root.right)+1;
    }
}

image-20210219155543933

110. 平衡二叉树

难度简单593

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

示例 1:

img

输入:root = [3,9,20,null,null,15,7]
输出:true

示例 2:

img

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

示例 3:

输入:root = []
输出:true

提示:

  • 树中的节点数在范围 [0, 5000]
  • -104 <= Node.val <= 104

思路

需要判断每个非叶子节点左右的深度差是否小于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 {
    public boolean isBalanced(TreeNode root) {
        if(root==null){
            return true;
        } 
        return Math.abs(getNodeDepth(root.left)-getNodeDepth(root.right))<=1
          &&isBalanced(root.left)
          &&isBalanced(root.right);
     }
       
    //获取当前节点的深度(注意深度是指最大的)
    private int getNodeDepth(TreeNode root){
        if(root==null){
            return 0;
        }else{
            return Math.max(getNodeDepth(root.left),getNodeDepth(root.right))+1;
        }
    }
}

257. 二叉树的所有路径

难度简单442

给定一个二叉树,返回所有从根节点到叶子节点的路径。

说明: 叶子节点是指没有子节点的节点。

示例:

输入:

   1
 /   \
2     3
 \
  5

输出: ["1->2->5", "1->3"]

解释: 所有根节点到叶子节点的路径为: 1->2->5, 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 {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> result = new ArrayList();
        dfs(root,result,new ArrayList<Integer>());
        return result;
    }

    private void dfs(TreeNode root,List<String> result,List<Integer> path){
        if(root==null){
            return;
        }
        path.add(root.val);
        //叶子节点
        if(root.left==null&&root.right==null){
            int length = path.size();
            StringBuffer sb = new StringBuffer();
            for(int i = 0;i<length;i++){
                sb.append(path.get(i));
                if(i!=length-1){
                    sb.append("->");
                }
            }
            result.add(sb.toString());
            //回溯
            path.remove(length-1);
            return;
        }
        dfs(root.left,result,path);
        dfs(root.right,result,path);
        //回溯
        path.remove(path.size()-1);
    }
}

404. 左叶子之和

难度简单280

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

示例:

    3
   / \
  9  20
    /  \
   15   7

在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

思路

首先什么是 左叶子:左节点+叶子节点

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int sum =0;
    public int sumOfLeftLeaves(TreeNode root) {
        sum(root);
        return sum;
    }
    private void sum(TreeNode root){
        if(root==null) return;
        if(root.left!=null){
            //左节点 是叶子节点
            if(root.left.left==null&&root.left.right==null){
                sum =sum+root.left.val;
            }
        }
        sum(root.left);
        sum(root.right);
        
    }
}

image-20210219222403470

513. 找树左下角的值

难度中等148

给定一个二叉树,在树的最后一行找到最左边的值。

示例 1:

输入:

    2
   / \
  1   3

输出:
1

示例 2:

输入:

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

输出:
7

注意: 您可以假设树(即给定的根节点)不为 NULL

思路

直接记录层序遍历最左边的即可:

/**
 * 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 {
    private int bottomLeftValue =0;
    public int findBottomLeftValue(TreeNode root) {
        Queue<TreeNode> queue = new ArrayDeque();
        queue.add(root);
        while(!queue.isEmpty()){
            int n = queue.size();
            for(int i =0;i<n;i++){
                TreeNode node = queue.poll();
                if(i==0){
                    bottomLeftValue=node.val;
                }
                if(node.left!=null){
                    queue.add(node.left);
                }
                if(node.right!=null){
                    queue.add(node.right);
                }
            }
        }
        return bottomLeftValue;
    }
}

112. 路径总和

难度简单512

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum

叶子节点 是指没有子节点的节点。

示例 1:

img

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true

示例 2:

img

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

示例 3:

输入:root = [1,2], targetSum = 0
输出:false

提示:

  • 树中节点的数目在范围 [0, 5000]
  • -1000 <= Node.val <= 1000
  • -1000 <= targetSum <= 1000

思路

这道题其实用回溯也能做,需要注意的是叶子节点的定义

/**
 * 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 hasPathSum(TreeNode root, int targetSum) {
        if(root==null) return false;
        if(root.left==null&&root.right==null){
            return targetSum==root.val;
        }
        return hasPathSum(root.left,targetSum-root.val)||hasPathSum(root.right,targetSum-root.val);
    }
}

![image-20210219225427781](/Users/frc/Library/Application Support/typora-user-images/image-20210219225427781.png)

113. 路径总和 II

难度中等425

给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。

说明: 叶子节点是指没有子节点的节点。

示例: 给定如下二叉树,以及目标和 sum = 22

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

返回:

[
   [5,4,11,2],
   [5,8,4,5]
]

思路

由于本题需要记录路径,所以在递归的基础上有回溯:

/**
 * 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<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> result = new ArrayList();
        if(root==null) return result;
        backtracking(root,targetSum,new ArrayList<Integer>(),result);
        return result;
    }

    private void backtracking(TreeNode root,int targetSum,List<Integer> path,List<List<Integer>> result){
        if(root==null){
            return;
        }
        path.add(root.val);
        if(root.left==null&&root.right==null){
            //当前root是叶子节点
            if(targetSum==root.val){
                result.add(new ArrayList(path));
            }
            //回溯
            path.remove(path.size()-1);
            return ;
        }else{
            backtracking(root.left,targetSum-root.val,path,result);
            backtracking(root.right,targetSum-root.val,path,result);
            //回溯
            path.remove(path.size()-1);
        }
        
    }
}