算法训练营day14 | {110.平衡二叉树, 257.二叉树的所有路径, 404.左叶子之和, 222.完全二叉树的节点个数}
第六章二叉树part03,思考什么题目适用什么遍历方式,练习递归三部曲。
110. 平衡二叉树
- 题目链接:110.平衡二叉树
- 文档讲解:代码随想录–110.平衡二叉树
- 视频讲解:后序遍历求高度,高度判断是否平衡 | LeetCode:110.平衡二叉树
- 状态:没做出来。
1.1 看完代码随想录的想法
- 平衡二叉树的定义:一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1。
- 因为是要求高度,所以用后序遍历比较合适,接着来思考思考递归三部曲:
- 参数是什么?返回值是什么? 传入一个节点,返回以这个节点为根节点的树的高度,如果这个树已经不平衡了,就返回-1。(体会后序遍历的好处)
- 确定终止条件。 当传入的节点是空时,返回高度为0。
- 确定单层递归的逻辑。 左右中,求该节点左子树和右子树的高度,如果某一个子树已经不平衡了,就不需要再计算另外一边,也不应该继续参与高度计算,剪枝!!然后判断两个子树的高度差是否大于1,如果大于一,那以该节点为子树的节点已经不平衡了,返回-1;否则符合平衡二叉树的定义,本层树的高度正常加1。
- 迭代法本轮精力有限,不去掌握了。
1.2 解题小结
- 第一遍写题没有考虑如果某一个子树已经不平衡的情况,导致计算了错误的高度差。修正了。
- 后序遍历▶110. 平衡二叉树
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class Solution {
public boolean isBalanced(TreeNode root) {
return getDepth(root) != -1;
}
public int getDepth(TreeNode node) {
if(node == null) return 0;
int leftHeight = getDepth(node.left);
if (leftHeight == -1) return -1;
int rightHeight = getDepth(node.right);
if (rightHeight == -1) return -1;
if(Math.abs(leftHeight - rightHeight) > 1) return -1;
return 1 + Math.max(leftHeight, rightHeight);
}
}
257. 二叉树的所有路径
- 题目链接:257. 二叉树的所有路径
- 文档讲解:代码随想录–257. 二叉树的所有路径
- 视频讲解:递归中带着回溯,你感受到了没?| LeetCode:257. 二叉树的所有路径
- 状态:用前序遍历比较合适吧,不过我不知道如何回溯。
2.1 看完视频的想法
视频给的是不隐藏回溯过程的代码写法,一些网上精简的题解会把回溯的过程隐藏掉(藏在函数调用时的参数赋值里)。
分析递归三部曲
- 参数和返回值是什么? 要传入根节点,记录一条路径用的数组path,和所有结果集的result,不需要返回值。
- 确定递归的终止条件。 当遍历到叶子节点递归终止,此时把path加入结果集。
- 确定单层递归的逻辑。 按照前序遍历(中左右)的顺序,遍历到哪个节点,就把哪个节点加入path路径,接着遍历左子树,遍历出来的时候要对path进行回溯,遍历右子树也是一样的要回溯。
细节
- 在写递归的时候,终止条件习惯了这么写:但是本题的终止条件这么写会很麻烦,因为null不一定是到了叶子节点,不如在找到叶子节点时,开始结束的处理逻辑。
1
2
3if (cur == null) {
终止处理逻辑
} - 回溯和递归是一一对应的,有一个递归,就要有一个回溯。
- 在写递归的时候,终止条件习惯了这么写:
2.2 解题小结
- 前序遍历–递归+回溯▶257. 二叉树的所有路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> ans = new ArrayList<>();
LinkedList<String> path = new LinkedList<>();
preOrder(root, path, ans);
return ans;
}
public void preOrder(TreeNode node, LinkedList<String> path, List<String> result) {
path.offerLast(String.valueOf(node.val)); // 中
if (node.left == null && node.right == null) {
result.add(String.join("->", path));
return;
}
if (node.left != null) {
preOrder(node.left, path, result);
path.pollLast();
}
if (node.right != null) {
preOrder(node.right, path, result);
path.pollLast();
}
}
}
404. 左叶子之和
- 题目链接:404.左叶子之和
- 文档讲解:代码随想录–404.左叶子之和
- 视频讲解:二叉树的题目中,总有一些规则让你找不到北 | LeetCode:404.左叶子之和
- 状态:用层序遍历做出来了,但递归的做法偷看了单层递归的文字后写出来了。
3.1 看完视频的想法
- 判断一个叶节点是否是左叶子节点的思路,要借助其父节点,和我想得一样。
- 本题适用于后序遍历,左右中,让根节点收集其左子树的左叶子之和和右子树的左叶子之和。分析递归三部曲:
- 参数和返回值是什么? 要传入根节点,返回值是这个二叉树的左叶子之和。
- 确定递归的终止条件。 当节点为空时,返回0,当节点本身为叶子节点时,也返回0。
- 确定单层递归的逻辑。 当遇到左叶子节点时,记录数值。然后通过递归求取左子树左叶子之和,和右子树左叶子之和,相加便是整个树的左叶子之和。
3.2 解题小结
后序遍历–递归法
▶404. 左叶子之和1
2
3
4
5
6
7
8
9
10class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if (root == null) return 0;
if (root.left == null && root.right == null) return 0;
int leftSum = sumOfLeftLeaves(root.left);
if (root.left != null && root.left.left == null && root.left.right == null) leftSum = root.left.val;
int rightSum = sumOfLeftLeaves(root.right);
return leftSum + rightSum;
}
}层序遍历-BFS
▶404. 左叶子之和1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class Solution {
public int sumOfLeftLeaves(TreeNode root) {
int sum = 0;
Deque<TreeNode> queue = new ArrayDeque<>();
if(root != null) queue.offerLast(root);
while(!queue.isEmpty()) {
int size = queue.size();
int curSum = 0;
while(size-- > 0) {
TreeNode node = queue.pollFirst();
if(node.left != null) {
if(node.left.left == null && node.left.right == null) curSum += node.left.val;
queue.offerLast(node.left);
}
if(node.right != null) queue.offerLast(node.right);
}
sum += curSum;
}
return sum;
}
}
222. 完全二叉树的节点个数
- 题目链接:222.完全二叉树的节点个数
- 文档讲解:代码随想录–222.完全二叉树的节点个数
- 视频讲解:要理解普通二叉树和完全二叉树的区别! | LeetCode:222.完全二叉树节点的数量
- 状态:我层序遍历唯手熟尔,本题适合后序遍历,判断左右子树是否是满二叉树,如果是就可以用公式。
4.1 解题分析
自己先分析一下递归三部曲(利用完全二叉树的性质):
- 参数和返回值是什么? 要传入根节点,返回以该节点为根节点的完全二叉树节点个数。
- 确定递归的终止条件。 当节点为空时,返回0;当以该节点为根节点的二叉树是一个满二叉树时,也作为终止条件。
- 确定单层递归的逻辑。 后序遍历,左右中,中间节点+1返回。
在写递归算法的时候,遇到一个关键问题:如何判断一个左子树或者右子树是否是二叉树呢?(看视频了)
- 一直向左、一直向右遍历,如果深度相同,则一定是一个满二叉树,可以画图分析。
4.2 解题小结
- 普通二叉树-后序遍历
和昨天做过的求树的最大深度、最小高度是一个模板。
▶
222. 完全二叉树的节点个数1 | |
- 普通二叉树-层序遍历(BFS)
这样动脑最少,但每个节点都要遍历,效率很低。而判断满二叉树可以减少对中间节点的遍历。
▶
222. 完全二叉树的节点个数1 | |
- 完全二叉树-后序遍历
▶
222. 完全二叉树的节点个数1 | |
5. 今日收获
- 求树的深度,可以转换为求根节点的高度,进而适用于后序遍历解题,今天继续练习了递归三部曲,然后遇到题目多思考怎么做为什么。
- 左叶子之和这道题,单层递归逻辑二刷时还需要自己来一遍哦。
- 学习时长:3小时
算法训练营day14 | {110.平衡二叉树, 257.二叉树的所有路径, 404.左叶子之和, 222.完全二叉树的节点个数}
http://paopaotangzu.xyz/cn/day14_leetcode/