第十六天| 第六章 二叉树part03 104.二叉树的最大深度 559.n叉树的最大深度 111.二叉树的最小深度 222.完全二叉树的节点个数
一、104.二叉树的最大深度
-
二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
-
二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)
-
题解给出的是:“本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。“
-
但是我认为使用后序遍历也可以求深度,具体地:
-
(1)使用后序遍历求深度
- 从根节点出发,记录从根节点出发左右子树的最大深度**(左右),然后将最大的深度返回给根节点(中)** ((左右)—> 中:左右收集信息返回给中)
- 代码:
class Solution { public int maxDepth(TreeNode root) { int depth = 0; return levelDepth(root, depth); } public int levelDepth(TreeNode root, int depth) { if (root == null) { return depth; } depth++; int leftDepth = levelDepth(root.left, depth); int rightDepth = levelDepth(root.right, depth); return Math.max(leftDepth, rightDepth); } }
-
(2)使用后序遍历求高度==(从叶子节点从0开始求高度)==
-
代码:
class Solution {
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;
}
}
以上两段代码的区别在于,求深度的时候遇到null,返回的是当前距离根节点深度depth;求高度的时候遇到null,从0开始记录当前的高度。
- 深度:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5sWrDkRv-1692881189749)(后序遍历求深度.png)]
- 高度:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c0EEU4rP-1692881189750)(后序遍历求高度.png)]
- (3)迭代:
class Solution {
public int maxDepth(TreeNode root) {
Deque<TreeNode> queue = new LinkedList<>();
int depth = 0;
if (root != null) {
queue.offer(root);
}
while (!queue.isEmpty()) {
int size = queue.size();
while (size-- > 0) {
TreeNode tempNode = queue.poll();
if (tempNode.left != null) {
queue.offer(tempNode.left);
}
if (tempNode.right != null) {
queue.offer(tempNode.right);
}
}
depth++;
}
return depth;
}
}
二、559.n叉树的最大深度
- (1)后序遍历求高度:
class Solution {
public int maxDepth(Node root) {
int max = 0;
if (root == null) {
return 0;
}
if (root.children != null) {
for (Node child : root.children) {
int childDepth = maxDepth(child);
max = max < childDepth ? childDepth : max;
}
}
return max + 1;
}
}
- (2)层序遍历,迭代:
class Solution {
public int maxDepth(Node root) {
Deque<Node> queue = new LinkedList<>();
int depth = 0;
if (root != null) {
queue.offer(root);
}
while (!queue.isEmpty()) {
int size = queue.size();
while (size-- > 0) {
Node tempNode = queue.poll();
for (Node child : tempNode.children) {
if (child != null) {
queue.offer(child);
}
}
}
depth++;
}
return depth;
}
}
三、111.二叉树的最小深度
- (1)递归(后序遍历)
class Solution {
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
int leftDepth = minDepth(root.left);
int rightDepth = minDepth(root.right);
if (root.left == null && root.right != null) {
return rightDepth + 1;
}
if (root.right == null && root.left != null) {
return leftDepth + 1;
}
return Math.min(leftDepth, rightDepth) + 1;
}
}
-
注意:和求最大深度的整体思路是一样的,但是这里要注意,这里的最小深度求的是**根节点到叶子节点的最小距离**(一定要注意是叶子节点),
-
什么是叶子节点,左右孩子都为空的节点才是叶子节点!
-
因此,要特殊处理以下情况:
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v9kVVLVz-1692881189750)(二叉树最小深度.png)]
-
这里如果直接进行下面三行的代码,就会把root节点的到它的左节点认为是最小深度
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KKhz3uC5-1692881189750)(最小深度代码.png)]
-
-
所以我们要判断,如果左空右不空,最小深度就是右子树的深度 + 1(返回给它们的根节点);如果右空左不空,最小深度就是左子树的深度 + 1(返回给它们的根节点);
-
(2)迭代:
class Solution {
public int minDepth(TreeNode root) {
Deque<TreeNode> queue = new LinkedList<>();
int depth = 0;
if (root != null) {
queue.offer(root);
}
while (!queue.isEmpty()) {
int size = queue.size();
while (size-- > 0) {
TreeNode tempNode = queue.poll();
if (tempNode.left != null) {
queue.offer(tempNode.left);
}
if (tempNode.right != null) {
queue.offer(tempNode.right);
}
if (tempNode.left == null && tempNode.right == null) {
depth++;
return depth;
}
}
depth++;
}
return depth;
}
}
四、222.完全二叉树的节点个数
-
题目链接:https://leetcode.cn/problems/count-complete-tree-nodes/
-
题目介绍:
- 给你一棵 完全二叉树 的根节点
root
,求出该树的节点个数。 - 完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第
h
层,则该层包含1~ 2h
个节点。
- 给你一棵 完全二叉树 的根节点
-
基础知识:
-
满二叉树
满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ni8GSe7f-1692881189751)(满二叉树.png)]
这棵二叉树为满二叉树,也可以说深度为k,有2^k-1个节点的二叉树。
-
完全二叉树
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。
我来举一个典型的例子如题:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K59toJVE-1692881189751)(完全二叉树.png)]
相信不少同学最后一个二叉树是不是完全二叉树都中招了。
之前我们刚刚讲过优先级队列其实是一个堆,堆就是一棵完全二叉树,同时保证父子节点的顺序关系。
-
-
思路:
- (1)不利用完全二叉树特性(适用于一般的二叉树):
class Solution { public int countNodes(TreeNode root) { if (root == null) { return 0; } int leftNums = countNodes(root.left); int rightNums = countNodes(root.right); return leftNums + rightNums + 1; } }
- (2)利用完全二叉树的特性(具体特性如下:)
- 首先,如果有一个满二叉树,节点个数 = 2 ^ (树的深度) - 1;
- 我们可以从这个点出发,找到完全二叉树中的满二叉树
- 在完全二叉树中,如何快速的判断子树是不是一个满二叉树呢?
- 只要保证从该节点遍历左子树得到的深度 == 从该节点遍历右子树得到的深度。比如下面这种情况:
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S7SONK4E-1692881189752)(image-20230824182630842.png)]
- 可能会有一个疑问,如下图打叉的位置万一没有元素呢?
- 事实上,这种情况是不可能出现在完全二叉树中的,如果蓝色可以遍历到最后的右子节点,说明打红叉的两个位置一定有元素,因为在完全二叉树的定义中,明确规定了:“最下面一层的节点都集中在该层最左边的若干位置”。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YkVKLZZT-1692881189752)(image-20230824182736322.png)]
- 在完全二叉树中,如何快速的判断子树是不是一个满二叉树呢?
class Solution {
public int countNodes(TreeNode root) {
// 1. 判断递归结束的条件
// 1.1 遇到null
// 1.2 如果左右子树的深度相同,返回该满二叉树的节点个数
// 1.1 遇到null
if (root == null) {
return 0;
}
// 1.2 如果左右子树的深度相同,返回该满二叉树的节点个数
TreeNode leftCur = root.left;
TreeNode rightCur = root.right;
int leftDepth = 1;
int rightDepth = 1;
while (leftCur != null) {
leftCur = leftCur.left;
leftDepth++;
}
while (rightCur != null) {
rightCur = rightCur.right;
rightDepth++;
}
if (leftDepth == rightDepth) {
return (int)Math.pow(2, rightDepth) - 1;
}
// 2. 每一层的逻辑
// 2.1 计算左子树的节点个数
// countNodes(root.left);
// 2.2 计算右子树的节点个数
// countNodes(root.right);
// 2.3 返回左 + 右 + 1
return countNodes(root.left) + countNodes(root.right) + 1;
}
}