目录
树的深度问题
在此之前,我们已经学习了很多树的基础知识和经典算法,今天让我们一起来学习几道比较有难度的题目——有关树的深度和高度问题,这几道对递归的要求高一些,一起来看看吧!
一 最大深度问题
给定一个二叉树,找出其最大深度。二叉树最大深度为根节点到最远叶子节点的最长路径上的节点数。例如下面的例子返回结果为最大深度为3.
3
/ \
9 20
/ \
15 7
实现方法其实很简单,只需要通过递归的方法一直遍历到root == null返回0,然后每遍历完当前节点的左右节点,返回左右子树较大的深度即可。递归遍历完所有节点则可得到该树的最大深度。实现代码如下:
public static int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
// 访问左节点查看左子树最大深度
int leftHeight = maxDepth(root.left);
// 访问右节点查看右子树最大深度
int rightHeight = maxDepth(root.right);
// 返回左右子树较大的深度加上当前节点作为该节点子树的最大深度
return Math.max(leftHeight, rightHeight) + 1;
}
当然除了这种做法,还有其他的解决思路,假如我们知道了树的层数那是不是就等于知道最大深度了。没错,所以哦我们只需要利用层序遍历的代码即可。代码如下:
public static int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
int ans = 0;
while (!queue.isEmpty()) {
//size表示某一层的所有元素数
int size = queue.size();
//size=0 表示一层访问完了
while (size > 0) {
TreeNode node = queue.poll();
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
// 每遍历完该层的一个节点,该节点出队,左右节点入队,该层节点数减一
size--;
}
// 每遍历完一层,则最大层数加1
ans++;
}
return ans;
}
二 最小深度
既然树有最大深度,那就会有最小深度。给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量,例如下面的例子返回结果3。
注!:最小深度的一层必须要有节点,最小深度指的是从根节点到最近叶子节点的最短路径上的节点数量!!!
这里的核心问题主要是分析终止条件:
⚪ 如果左右子树都为空,则说明该节点为叶子节点,返回其初始深度值1。
⚪ 如果左子树为空,右子树不为空,则最小深度为右子树深度+1。
⚪ 如果右子树为空,左子树不为空,则最小深度为左子树深度+1。
⚪ 如果左右子树都不为空,返回左右子树深度的最小值 + 1,则得到当前子树最小深度
代码如下:
public int minDepth(TreeNode root) {
if (root == null) return 0;
// 如果遍历到叶子节点,则返回当前节点深度1
if(root.left == null && root.right == null) return 1;
int mindepth = Integer.MAX_VALUE; // 存储当前最小子树深度
// 遍历左右子树,寻找当前最小深度
if(root.left != null) mindepth = Math.min(mindepth, minDepth(root.left));
if(root.right != null) mindepth = Math.min(mindepth, minDepth(root.right));
// 返回最小子树深度,为该节点的最小子树深度加上本身深度1
return mindepth + 1;
}
上面的方法是通过递归从叶子节点出发,不断判断子树的最小深度,最终得到整棵树的最小深度。除了这个方法,我们同样也可用层次遍历来解决,只要在层次遍历时第一次遇到子节点直接返回其所在层次即可,所以只需改一下层次遍历的代码即可,如下:
public static int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
int minDepth = 0;
LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
while (queue.size() > 0) {
//获取当前队列的长度,这个长度相当于 当前这一层的节点个数
int size = queue.size();
minDepth++;
for (int i = 0; i < size; ++i) {
TreeNode t = queue.remove();
if (t.left == null && t.right == null) {
// 当遍历到叶子节点,返回当前的层数,即为最小深度
return minDepth;
}
if (t.left != null) {
queue.add(t.left);
}
if (t.right != null) {
queue.add(t.right);
}
}
}
return 0;
}
三 N 叉树的最大深度
前面我们研究的都是二叉树,那要是换成N叉树呢,其实也是大差不差的,只不过树结构改变了而已,原来是遍历左右孩子,现在需要遍历节点的所有子节点,也就是遍历该节点所有子节点的列表而已,遍历时多加了个List的for循环。具体实现代码如下:
class Solution {
public int maxDepth(Node root) {
if(root == null) return 0;
// 遍历到叶子节点,返回当前一个深度
else if(root.children.isEmpty()) return 1;
else{
// 若不是叶子节点,则要再访问自己的所有子节点
List<Integer> heights = new LinkedList<>(); // 存放每个子节点的深度
for (Node node : root.children){
heights.add(maxDepth(node));
}
return Collections.max(heights) + 1; // 返回当前子树最大深度
}
}
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) {
val = _val;
children = _children;
}
}
}