二叉树的深度和高度问题-算法通关村
1 最大深度问题
-
LeetCode104: 给定一个二叉树,找出其最大深度。
-
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明:叶子节点是指没有子节点的节点。 -
对于node(3),最大深度自然是左右子结点+1,左右子节点有的可能为空,只要有一个,树的最大高度就是1+1=2。然后再增加几个结点:
-
很显然相对于node(20),最大深度自然是左右子结点+1,左右子节点有的可能为空,只要有一个,树的最大高度就是1+1=2,用代码表示就是:
-
int depth = 1 + max(leftDepth, rightDepth);
而对于3,则是左右子树深度最大的那个然后再+1,具体谁更大,则不必关心。所以对于node(3)的判断逻辑就是: -
int leftDepth = getDepth(node, left);//左
int rightDepth = getDepth(node, right)//右
int depth = 1 + max(leftDepth, rightDepth);//中 -
那什么时候结束呢,这里仍然是root == null返回0就行了。至于入参,自然是要处理的子树的根节点root,而返回值则是当前root所在子树的最大高度。所以合在一起就是:
-
public int maxDepth(TreeNode root){ if(root == null){ return 0; } int leftDepth = maxDepth(root.left); int rightDepth = maxDepth(root.right); return Math.max(leftDepth, rightDepth) + 1; }
-
上面代码先拿到左右子树的结果再计算Math.max(left, right)+ 1,这与后序遍历本质上一样的,因此可以看做后序遍历的拓展问题。
我们继续分析这个题,如果确定树最大有几层是不是也就知道最大深度了?是的,直接套用层序遍历的代码就可以。 -
public int maxDepth2(TreeNode root){ if(root == null){ return 0; } Deque<TreeNode> queue = new ArrayDeque<>(); queue.offer(root); int depth = 0; while(!queue.isEmpty()){ for(int i = 0; i < queue.size(); i++){ TreeNode temp = queue.poll(); if(temp.left != null){ queue.offer(temp.left); } if(temp.right != null){ queue.offer(temp.right); } } depth++; } return depth; }
2 判断平衡二叉树
-
LeetCode110 判断平衡二叉树:给定一个二叉树,判断它是否是高度平衡的二叉树。本题中,一棵高度平衡二叉树定义为:一个二叉树**每个节点** 的左右两个子树的高度差的绝对值不超过1。
这里的要求是二又树每个节点的左右两个子树的高度差的绝对值不超过1。先补充一个问题,高度和深度怎么区分呢?
• 二又树节点的深度:指从根节点到该节点的最长简单路径边的条数。
• 二又树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。
直接看图: -
关于根节点的深度究竟是1还是0,不同的地方有不一样的标准,leetcode的题目中都是以根节点深度是1,其他的就不管了。
言归正传,我们仍然先看一个最简单的情况: -
很显然只有两层的时候一定是平衡的,因为对于node(3),左右孩子如果只有一个,那高度差就是1;如果左右子孩子都有或者都没有,则高度差为0。如果增加一层会怎么样呢?如下图:
-
对于node(3),需要同时知道自己左右子树的最大高度差是否<2。
- 当节点root 左 / 右子树的高度差<2,则返回节点 root 的左右子树中最大高度加1(max(left, right) + 1);参考上面的高度和深度的对比图思考一下,这里为什么是最大高度?
- 当节点root 左/右子树的高度差 ≥2:则返回-1,代表 此子树不是平衡树。
也就是:
-
int left = recur(root.left);
int right = recur(root.right);
return Math.abs(left-right) < 2 ? Math.max(left, right) + 1 : -1; -
这就是核心递归,假如子树已经不平衡了,则不需要再递归了直接返回就行,比如下面树中的结点4
-
class BalancedTree{ public boolean isBalanced(TreeNode root){ return height(root) >= 0; } public int height(TreeNode root){ if(root == null){ return 0; } int leftDepth = height(root.left); int rightDepth = height(root.right); if(leftDepth == -1 || rightDepth == -1 || Math.abs(leftDepth-rightDepth) > 1){ return -1; }else{ return Math.max(leftDepth, rightDepth) +1; } } }
3 最小深度
-
既然有最大深度,那是否有最小深度呢?LeetCode111:给定一个二叉树,找出其最小深度。最小深度是**从根节点到最近叶子节点的**最短路径上的节点数量,例如下面的例子返回结果为 3 。说明:叶子节点是指没有子节点的节点。
-
这里的关键问题是题目中说的:最小深度是从根节点到最近叶子节点的最短路径上的节点数量,也就是最小深度的一层必须要有叶子结点。
-
这里的核心问题仍然是分析终止条件:
- 如果左子树为空,右子树不为空,说明最小深度是1+右子树的深度。
- 反之,右子树为空,左子树不为空,最小深度是1+左子树的深度。
- 最后如果左右子树都不为空,返回左右子树深度最小值+1。
代码如下:
-
public int minDepth(TreeNode root){ if(root == null){ return 0; } //说明root是一个叶子节点,其最小深度为1 if(root.left == null && root.right == null){ return 1; } //为了确保在比较时,任何实际的深度值都会比这个初始值小。 int min_depth = Integer.MAX_VALUE; if(root.left != null){ min_depth = Math.min(minDepth(root.left), min_depth); } if(root.right != null){ min_depth = Math.min(minDepth(root.right), min_depth); } return min_depth + 1; }
-
除了递归方式,我们也可使用层次遍历,只要遍历时,第一次遇到叶子就直接返回其所在的层次即可。
-
public int minDepth2(TreeNode root){ if(root == null){ return 0; } Deque<TreeNode> queue = new ArrayDeque<>(); queue.offer(root); int minDepth = 0; while(!queue.isEmpty()){ //将minDepth增加1,表示当前遍历的深度增加了一层 minDepth++; for(int i = 0; i < queue.size(); i++){ TreeNode temp = queue.poll(); if(temp.left == null && temp.right == null){ return minDepth; } if(temp.left != null){ queue.offer(temp.left); } if(temp.right != null){ queue.offer(temp.right); } } } return 0; }
4 N叉树的最大深度
-
如果将二叉树换成N叉树又该怎么做呢?
-
LeetCode559.给定一个 N 叉树,找到其最大深度。最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。
N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔(请参见示例)。 -
示例1:
输入:root = [1, null, 3, 2, 4, null, 5, 6]
输出:3
示例2:
输入: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 -
这道题就是将二叉树换成了N叉树,不同点就在于N叉树节点比较多,我们使用list,遍历时用for即可。
-
N叉树的定义:
-
public class Node { public int val; public List<Node> children; public Node() { } public Node(int val) { this.val = val; } public Node(int val, List<Node> children) { this.val = val; this.children = children; } }
-
public int maxDepthNTree(Node root){ if(root == null){ return 0; }else if(root.children.isEmpty()){ //当前节点是一个叶子节点,其最大深度为1,直接返回1。 return 1; }else{ List<Integer> heights = new ArrayList<>(); for (Node child : root.children) { heights.add(maxDepthNTree(child)); } return Collections.max(heights) + 1; } }