算法通关村系列文章目录
前言
本系列文章是针对于鱼皮知识星球——编程导航中的算法通关村中的算法进行归纳和总结。 该篇文章讲解的是第八中的白银挑战和黄金挑战———二叉树的深度和高度问题
在这一期中,我们主要是来看一下二叉树的深度和高度的有关问题,并且在题目中,我们会大量使用递归的思想来处理问题,所以,本期也会再着重复习一下在二叉树问题中,递归的基本思路和构造方式的问题
反向后,节点原来连接的其他节点如何处理
经典的链表反转方法有头插法和直接反向法
一、最大深度问题
LeetCode 104:
给定一个二叉树 root ,返回其最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数
输入:root = [3,9,20,null,null,15,7]
输出:3
后面几道题我们都通过递归的思想来做
步骤:
- 对于每一个节点来说,由他开头的最大路径一定是,左子树和右子树中路径较大的那个路径+1.
- 如果对于一个节点,他的左节点和右节点都是null,那么他就是叶子节点,同时也是一个路径的终点,这时候我们只要返回1即可
按我们上述思路可以很轻松的写出代码
public static int maxD(TreeNode root){
if(root.left==null&&root.right==null){
return 1;
}
int left=0;
int right=0;
if(root.left!=null){
left= maxD(root.left) + 1;
}
if(root.right!=null){
right=maxD(root.right)+1;
}
return Math.max(left,right);
}
从这我们也可以管中窥豹,只要搞清递归每一次要做的事情,把终止条件想清后,对于一般的递归题目还是很容易的
二、判断平衡树
LeetCode 110:
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1
输入:root = [3,9,20,null,null,15,7]
输出:true
步骤:
- 对于每一个节点来说,先把left,right依次递归得到,左子树的高度和右子树的高度,如果其中任意一个返回-1,就说明,左子树或右子树已经不平衡了,那对于这个节点来说,自然也就不平衡了,直接返回-1就好了
- 如果都不是-1,那就进入比较,如果高度差<=1的话,那么符合要求,最后返回最大的那个再加个1,如果>1了,那就符合了,直接返回-1
- 如果遍历到叶子节点了,那就没法往后递归了,直接返回1即可。
public static int recur(TreeNode root){
if(root.left==null&&root.right==null){
return 1;
}
int left=0;
int right=0;
if(root.left!=null) left = recur(root.left);
if(left==-1) return -1;
if(root.right!=null) right= recur(root.right);
if(right==-1) return -1;
return Math.abs(right-left)<=1?Math.max(left,right)+1:-1;
}
转换为代码
main
三、最小深度
LeetCode 111:
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
输入:root = [3,9,20,null,null,15,7]
输出:2
这个最小深度,跟我们上面做的最大深度的思路都是差不多的,只是在初始化变量的时候,要设置成 Integer.MAX_VALUE,不能再设置为0了。
步骤:
- 对于每一个节点来说,以它开头的最短路径一定是,左子树和右子树中相较较小的那个路径+1即可。
- 当遍历到叶子节点,没有办法再往下遍历,就直接返回1即可
代码:
public static int minDepth(TreeNode root){
if(root==null) return 0;
if(root.left==null&&root.right==null){
return 1;
}
int left=Integer.MAX_VALUE,right=Integer.MAX_VALUE;
if(root.left!=null) left=minDepth(root.left);
if(root.right!=null) right=minDepth(root.right);
return Math.min(left,right)+1;
}
四、N叉树的最大深度
LeetCode 559:
给定一个 N 叉树,找到其最大深度。
最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。
N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔(请参见示例)。
输入:root = [1,null,3,2,4,null,5,6]
输出:3
这里它只是把原来的二叉树换成了N叉树,其本质还是一样,只是原来只需要比较两个路径,这里需要需要比较N条路径,其他的都一样
步骤:
- 对于每个节点来说,以它开头的最大路径,就是它的N个字节点开头的路径中最大的那个路径+1即可
- 如果遍历的节点是叶子节点的话,说明没法再往下遍历了,直接返回1即可
public static int maxDepth_N(NTreeNode root) {
if(root==null) return 0;
if(root.children==null){
return 1;
}
int max=0;
for (NTreeNode child : root.children) {
max=Math.max(maxDepth_N(child),max);
}
return max+1;
}
五、最近公共祖先问题
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
这道题就比较难了,就是递归中,每一层该做的事情不好区分了。我们先来看,题目要求我们找到规定的两节点的最近公共祖先。通过分析题目,我们会发现,最近的公共祖先的情况有:
- 两个节点本身中的一个
- 两个节点前面第一个分叉的节点
那对于这道题,我们可以这样想
- 对于每一个节点来说,如果他自身就是目前节点中的某一个,说明他就找到了目标节点,也就不用再往下找了,直接返回该目标节点的值,说明,对于这个分支来说,他找到了正确的路径,并且最近的祖先是返回的值。
- 相反,如果遍历到最后仍然没有找到,那就直接返回null,代表这一分支没有目标元素,直接舍弃即可。
- 那么当左右节点都返回后,就该比较了,如果左侧为null,右侧为null,说明对于该节点来说,他的左子树和右子树都没有目标节点,直接返回null就好了
- 如果左侧为null,右侧不为null,那就返回右侧的元素
- 同理右侧为null,左侧不为null,那就返回左侧的元素
- 如果左侧和右侧都返回了,那该节点不就是最近的公共祖先了嘛,那就返回他本身
我们按照思路很容易就把代码顺下来了
public static TreeNode lowestCommonAncestor(TreeNode node,TreeNode p, TreeNode q){
//这个并不是一个将节点连起来的操作,只是去看一看该节点的左树和右树,有没有符合情况的,如果left是null。说明左树没有符合情况的,right也同理。
//如果左边返回的不是努null,那就说明左侧有,并且返回的还要啥最近的节点。
if(node==null||node.val==p.val||node.val==q.val) return node;
TreeNode left = lowestCommonAncestor(node.left,p,q);
TreeNode right = lowestCommonAncestor(node.right, p,q);
if(left==null&&right==null) return null;
if(left==null) return right;
if(right==null) return left;
return node;
}
总结
当你好好分析了上面的几道题后,会发现上面的那几道都是有相同的思路的,对于每一个节点,都是要分别去对左节点和右节点分别遍历,并对这两个结果进行比较处理,最后返回就好了。本节的题大家一定要好好分析,这些不仅是二叉树的基础问题,同样也是我们递归思路养成的很好的题目。
那本节的内容就到此结束。我是Mayphyr,从一点点到亿点点,我们下次再见