104.二叉树的最大深度 (优先掌握递归)什么是深度,什么是高度,如何求深度,如何求高度,这里有关系到二叉树的遍历方式。
大家 要先看视频讲解,就知道以上我说的内容了,很多录友刷过这道题,但理解的还不够。
题目链接/文章讲解/视频讲解: 代码随想录
求高度是后序遍历,求深度是前序遍历;根节点的高度就是最大深度,所以可以用后序遍历
本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。前序复杂很多,涉及到回溯。
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)
而根节点的高度就是二叉树的最大深度,所以本题中我们通过后序求的根节点高度来求的二叉树最大深度。
- 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。
- 确定终止条件:如果为空节点的话,就返回0,表示高度为0。
- 确定单层递归的逻辑:先求它的左子树的深度,再求右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
前序:充分表现出求深度回溯的过程
回溯的目的是在递归回退到上一层时,将之前增加的深度值减去,以便正确计算每个子树的深度。当递归遍历完左子树后,需要回溯(深度减1)到当前层的深度,然后继续遍历右子树。同样,在遍历完右子树后,也需要进行回溯操作。这是因为递归调用时使用的是同一个深度变量,为了保证每次递归都能正确计算深度,需要在递归返回到上一层时将之前增加的深度值减去。
559.n叉树的最大深度
给定一个 n 叉树,找到其最大深度。
最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。
注意+1的位置(在这段代码中,不能在递归调用的地方执行 +1 操作,而是在比较所有子节点高度并选择最大值后进行加一操作。这样可以保持正确的高度计算,并返回正确的结果。)
/*
// 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 {
public int maxDepth(Node root) {
return getHeight(root);
}
public int getHeight(Node node){
if(node == null) return 0;
int highest = 0;
for(Node children: node.children){
int height = getHeight(children);
highest = highest > height? highest : height;
}
return highest + 1;
}
}
111.二叉树的最小深度 (优先掌握递归)先看视频讲解,和最大深度 看似差不多,其实 差距还挺大,有坑。
题目链接/文章讲解/视频讲解:代码随想录
题目中说的是:最小深度是从根节点到最近叶子节点的最短路径上的节点数量。,注意是叶子节点。
左右孩子都为空的节点才是叶子节点!
- 确定递归函数的参数和返回值:参数为要传入的二叉树根节点,返回的是int类型的深度。
- 确定终止条件:终止条件也是遇到空节点返回0,表示当前节点的高度为0。
- 确定单层递归的逻辑
误区:
按照求最大深度的方法,只是把max改为min,会导致:
没有左孩子的分支会算为最短深度。
所以,如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。
反之,右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。 最后如果左右子树都不为空,返回左右子树深度最小值 + 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 {
/**
* 递归法,相比求MaxDepth要复杂点
* 因为最小深度是从根节点到最近**叶子节点**的最短路径上的节点数量
*/
public int minDepth(TreeNode root) {
return getHeight(root);
}
public int getHeight(TreeNode node){
if(node == null) return 0;
int leftTree = getHeight(node.left);// 左
int rightTree = getHeight(node.right);// 右
// 中
// 当一个左子树为空,右不为空,这时并不是最低点
if(node.left == null && node.right != null){
return 1 + rightTree;
// 当一个右子树为空,左不为空,这时并不是最低点
}else if(node.left != null && node.right == null){
return 1 + leftTree;
}else{// 左右结点都不为null
return 1 + Math.min(leftTree, rightTree);
}
}
}
222.完全二叉树的节点个数(优先掌握递归)需要了解,普通二叉树 怎么求,完全二叉树又怎么求
题目链接/文章讲解/视频讲解:代码随想录
普通二叉树,递归 前中后序遍历、层序遍历都可以统计树中节点的数量。模板题
这道题目的递归法和求二叉树的深度写法类似, 而迭代法,二叉树:层序遍历登场! (opens new window)遍历模板稍稍修改一下,记录遍历的节点数量就可以了。
递归遍历的顺序依然是后序(左右中),时间复杂度:O(n),所有节点都遍历了一遍
/**
* 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) {
return getNum(root);
}
public int getNum(TreeNode node){
if(node == null) return 0;
int leftnum = getNum(node.left);
int rightnum = getNum(node.right);
return 1 + leftnum + rightnum;
}
}
完全二叉树:需要使用完全二叉树特性
完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。
对于情况一,可以直接用 2^树深度 - 1 来计算,注意这里根节点深度为1。
对于情况二,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。
如果整个树不是满二叉树,就递归其左右孩子,直到遇到满二叉树为止,用公式计算这个子树(满二叉树)的节点数量。
在完全二叉树中,如果递归向左遍历的深度等于递归向右遍历的深度,那说明就是满二叉树。
时间复杂度:O(log n × log n),并没有去完全遍历完全二叉树中所有的节点
/**
* 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) {
return getNum(root);
}
public int getNum(TreeNode node){
if(node == null) return 0;
// 第二步:终止条件的写法应该是这样的:
// 开始根据左深度和右深度是否相同来判断该子树是不是满二叉树
TreeNode left = node.left;
TreeNode right = node.right;
// 这里初始为0是有目的的,为了下面求指数方便(实际上从0开始,但计算时相当于从1开始)
int leftDepth = 0;
int rightDepth = 0;
while(left != null){// 求左子树深度
left = left.left;
leftDepth++;
}
while(right != null){// 求右子树深度
right = right.right;
rightDepth++;
}
if(leftDepth == rightDepth){
return (2 << leftDepth) - 1;// 注意(2<<1) 相当于2^2,返回满足满二叉树的子树节点数量
}
// 第三步,单层递归的逻辑
int leftTreeNum = getNum(node.left);// 左
int rightTreeNum = getNum(node.right);// 右
int result = leftTreeNum + rightTreeNum + 1;// 中 加上当前节点自己
return result;
}
}