104. 二叉树的最大深度
题目
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回它的最大深度 3 。
解法
方法一:深度优先搜索
如果我们知道了左子树和右子树的最大深度 l 和 r,那么该二叉树的最大深度即为
max(l,r)+1
而左子树和右子树的最大深度又可以以同样的方式进行计算。因此我们可以用「深度优先搜索」的方法来计算二叉树的最大深度。具体而言,在计算当前二叉树的最大深度时,可以先递归计算出其左子树和右子树的最大深度,然后在 O(1)时间内计算出当前二叉树的最大深度。递归在访问到空节点时退出。
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
方法二:广度优先搜索
我们也可以用「广度优先搜索」的方法来解决这道题目,但我们需要对其进行一些修改,此时我们广度优先搜索的队列里存放的是「当前层的所有节点」。
每次拓展下一层的时候,不同于广度优先搜索的每次只从队列里拿出一个节点,我们需要将队列里的所有节点都拿出来进行拓展,这样能保证每次拓展完的时候队列里存放的是当前层的所有节点,即我们是一层一层地进行拓展,最后我们用一个变量ans 来维护拓展的次数,该二叉树的最大深度即为ans。
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
int ans = 0;
while (!queue.isEmpty()) {
int size = queue.size();
// 把这一层的都依次取出
while (size-- > 0) {
TreeNode node = queue.remove();
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
ans++;
}
return ans;
}
111. 二叉树的最小深度
题目
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
输入:root = [3,9,20,null,null,15,7]
输出:2
示例 2:
输入:root = [2,null,3,null,4,null,5,null,6]
输出:5
解法
深度优先搜索
叶子节点的定义是左孩子和右孩子都为 null 时叫做叶子节点
当 root 节点左右孩子都为空时,返回 1
当 root 节点左右孩子有一个为空时,返回不为空的孩子节点的深度
当 root 节点左右孩子都不为空时,返回左右孩子较小深度的节点值
public int minDepth(TreeNode root) {
if(root == null) return 0;
//这道题递归条件里分为三种情况
//1.左孩子和有孩子都为空的情况,说明到达了叶子节点,直接返回1即可
if(root.left == null && root.right == null) return 1;
//2.如果左孩子和由孩子其中一个为空,那么需要返回比较大的那个孩子的深度
int m1 = minDepth(root.left);
int m2 = minDepth(root.right);
//这里其中一个节点为空,说明m1和m2有一个必然为0,所以可以返回m1 + m2 + 1;
if(root.left == null || root.right == null) return m1 + m2 + 1;
//3.最后一种情况,也就是左右孩子都不为空,返回最小深度+1即可
return Math.min(m1,m2) + 1;
}
广度优先搜索
同样,我们可以想到使用广度优先搜索的方法,遍历整棵树。
当我们找到一个叶子节点时,直接返回这个叶子节点的深度。广度优先搜索的性质保证了最先搜索到的叶子节点的深度一定最小。
public class Solution {
class QueueNode {
TreeNode node;
int depth;
public QueueNode(TreeNode node, int depth) {
this.node = node;
this.depth = depth;
}
}
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
Queue<QueueNode> queue = new LinkedList<>();
queue.add(new QueueNode(root, 1));
while (!queue.isEmpty()) {
QueueNode nodeDepth = queue.remove();
TreeNode node = nodeDepth.node;
int depth = nodeDepth.depth;
if (node.left == null && node.right == null) {
return depth;
}
if (node.left != null) {
queue.add(new QueueNode(node.left, depth + 1));
}
if (node.right != null) {
queue.add(new QueueNode(node.right, depth + 1));
}
}
return 0;
}
}
559. N 叉树的最大深度
题目
给定一个 N 叉树,找到其最大深度。
最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。
N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔(请参见示例)
输入:root = [1,null,3,2,4,null,5,6]
输出:3
解答
深度优先搜索
解决这个问题的最直观方法就是递归。
递归去求每个子节点的最大深度,返回其中最大的子树深度+1
不同的是,二叉树中求的是左子树和右子树的最大值。
public int maxDepth(Node root) {
if (root == null) {
return 0;
}
// 如果没有子树 则为1
if (root.children.isEmpty()) {
return 1;
}
// 递归去求每个子节点的最大深度
List<Integer> heights = new ArrayList<>();
for (Node item : root.children) {
heights.add(maxDepth(item));
}
// 返回其中最大的深度+1
return Collections.max(heights) + 1;
}
迭代
我们也可以用「广度优先搜索」的方法来解决这道题目,但我们需要对其进行一些修改,此时我们广度优先搜索的队列里存放的是「当前层的所有节点」。
每次拓展下一层的时候,不同于广度优先搜索的每次只从队列里拿出一个节点,我们需要将队列里的所有节点都拿出来进行拓展,这样能保证每次拓展完的时候队列里存放的是当前层的所有节点,即我们是一层一层地进行拓展,最后我们用一个变量ans 来维护拓展的次数,该二叉树的最大深度即为ans。
public int maxDepth(Node root) {
if (root == null) {
return 0;
}
Queue<Node> queue = new LinkedList<>();
queue.add(root);
int depth = 0;
while (!queue.isEmpty()) {
int n = queue.size();
depth++;
// 每一层的所有节点都遍历一遍
while (n-- != 0) {
// 所有子节点都入队
for (Node item : queue.remove().children) {
queue.add(item);
}
}
}
return depth;
}
110. 平衡二叉树
题目
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
输入:root = [1,2,2,3,3,null,null,4,4]
输出:false
解法
从底至顶(提前阻断)
此方法为本题的最优解法,但“从底至顶”的思路不易第一时间想到。
思路是对二叉树做先序遍历,从底至顶返回子树最大高度,若判定某子树不是平衡树则 “剪枝” ,直接向上返回。
算法流程:
recur(root):
-
递归返回值:
-
- 当节点root 左 / 右子树的高度差 < 2:则返回以节点root为根节点的子树的最大高度,即节点 root 的左右子树中最大高度加 1( max(left, right) + 1 );
-
- 当节点root 左 / 右子树的高度差 ≥2 :则返回 −1 ,代表 此子树不是平衡树 。
-
递归终止条件:
-
- 当越过叶子节点时,返回高度 0 ;
-
- 当左(右)子树高度 left== -1 时,代表此子树的 左(右)子树 不是平衡树,因此直接返回 -1 ;
public boolean isBalanced(TreeNode root) {
return recur(root) != -1;
}
public int recur(TreeNode root) {
// 叶子节点高度为0
if (root == null) {
return 0;
}
// 如果不平衡则直接返回
int leftHeight = recur(root.left);
if (leftHeight == -1) {
return -1;
}
int rightHeight = recur(root.right);
if (rightHeight == -1) {
return -1;
}
// 判断是否高度相差在2以内
return Math.abs(leftHeight - rightHeight) < 2 ? Math.max(leftHeight, rightHeight) + 1 : -1;
}
从顶至底(暴力法)
此方法容易想到,但会产生大量重复计算,时间复杂度较高。
思路是构造一个获取当前节点最大深度的方法 depth(root) ,通过比较此子树的左右子树的最大高度差abs(depth(root.left) - depth(root.right)),来判断此子树是否是二叉平衡树。若树的所有子树都平衡时,此树才平衡。
class Solution {
public boolean isBalanced(TreeNode root) {
if (root == null) return true;
return Math.abs(depth(root.left) - depth(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
}
private int depth(TreeNode root) {
if (root == null) return 0;
return Math.max(depth(root.left), depth(root.right)) + 1;
}
}