Day13 力扣二叉树 : 层序遍历| 226.翻转二叉树 (优先掌握递归|101. 对称二叉树 (优先掌握递归)
层序遍历
层序遍历
看完本篇可以一口气刷十道题,试一试, 层序遍历并不难,大家可以很快刷了十道题。
题目链接/文章讲解/视频讲解:https://programmercarl.com/0102.%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%B1%82%E5%BA%8F%E9%81%8D%E5%8E%86.html
102.二叉树的层序遍历
第一印象:
层序遍历就是广度优先搜索,我知道他的过程,但是代码实现我不会
看完题解的思路:
我觉得关键就在于size的变化,只有控制好size才能确定队列每层 poll 出几个元素,看一遍讲解我没明白 size 是怎么变的。
将一层的节点加入队列,先记录队列大小 size, 再对每个节点做操作,即将每个节点的左右节点加入队列。一轮结束后,本层节点全都 poll 了,他下一层的节点全在队列内。再重新开始一轮,先记录 size …
如此循环往复就是size变化的规律,一层放进去后就立刻记录size,对每个节点操作的同时, size递减,size就代表这层有几个节点,size == 0 的时候本层节点也就处理完了。
实现中的困难:
前文提到过栈与队列用 Deque
我发现Deque作为栈用就是 push pop
作为队列用就是 add poll。
感悟:
我去试试层序遍历的十道题,会的就不写在题解里了。
贴上代码:
class Solution {
public List<List<Integer>> result = new ArrayList<List<Integer>>();
public List<List<Integer>> levelOrder(TreeNode root) {
eachlevel(root);
return result;
}
public void eachlevel(TreeNode node) {
if (node == null) return;
Deque<TreeNode> que = new LinkedList<>();
que.add(node);
while (!que.isEmpty()) {
int size = que.size();
List<Integer> item = new ArrayList<>();
while (size-- != 0) {
TreeNode cur = que.poll();
item.add(cur.val);
if (cur.left != null) que.add(cur.left);
if (cur.right != null) que.add(cur.right);
}
result.add(item);
}
}
}
107.二叉树的层次遍历2
这道题就是从下层向上层次遍历,但是每一层还是从左到右的。所以只需要反转一下102题的答案,但是题解中有个用链表的方式我觉得比较奇特。
记录一下:
用链表的原因是利用链表可以进行 O(1) 头部插入, 这样最后答案不需要再反转
而链表的声明竟然是
LinkedList<List<Integer>> ans = new LinkedList<>();
那我做链表章节的时候为什么要自己定义链表,直接用啊md(ο´・д・)??
199.二叉树的右视图
这道题就是收集每层最右边的节点(从上向下)
只需要判断是不是当前层最后一个就行,也就是判断 size 之后再收集。
while (size-- != 0) {
TreeNode cur = que.poll();
if (size == 0) result.add(cur.val); //这行关键捏
if (cur.left != null) que.add(cur.left);
if (cur.right != null) que.add(cur.right);
}
一开始我写成了size == 1,是不对的,因为最后一个节点 size = 1 进入while循环后会减1,变成0.
637.二叉树的层平均值
思路很简单,记录两个实现时候遇到的错误。
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<Double> result = new ArrayList<>();
Deque<TreeNode> que = new LinkedList<>();
if (root == null) return result;
que.add(root);
while (!que.isEmpty()) {
int size = que.size();
int cnt = size;
Double sum = 0.0;
//每层
while (size-- != 0) {
TreeNode cur = que.poll();
sum += cur.val;
if (size == 0) {
double avr = sum / cnt;
result.add(avr);
}
if (cur.left != null) que.add(cur.left);
if (cur.right != null) que.add(cur.right);
}
}
return result;
}
}
sum 的声明是在 while (size-- != 0)
之外,不然每处理一个节点 sum 都会先归零。 其实就是明确这个while 循环内是每层的逻辑,所以对于 sum 这个一层的和来说,要声明在外面。
第二个问题是我一开始声明sum是int型,就会有类型不匹配。 否则
double avr = (double) sum / cnt;
也可以
429.N叉树的层序遍历
就是一个节点可能有很多个孩子,去遍历一下孩子就可以了。
class Solution {
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> result = new ArrayList<>();
Deque<Node> que = new LinkedList<>();
if (root == null) return result;
que.add(root);
while (!que.isEmpty()) {
int size = que.size();
List<Integer> item = new ArrayList<>();
//对每一层的操作
while (size-- != 0) {
Node cur = que.poll();
//记录数值
item.add(cur.val);
//遍历孩子 加入que
if (cur.children != null) {
for (Node child : cur.children) {
que.add(child);
}
}
}
result.add(item);
}
return result;
}
}
515.在每个树行中找最大值
比较简单
class Solution {
public List<Integer> largestValues(TreeNode root) {
List<Integer> res = new ArrayList<>();
Deque<TreeNode> que = new LinkedList<>();
if (root == null) return res;
que.add(root);
while (!que.isEmpty()) {
int size = que.size();
int max = Integer.MIN_VALUE;
while (size-- != 0) {
TreeNode cur = que.poll();
if (cur.val > max) max = cur.val;
if (cur.left != null) que.add(cur.left);
if (cur.right != null) que.add(cur.right);
}
res.add(max);
}
return res;
}
}
116.填充每个节点的下一个右侧节点指针
也不难
class Solution {
public Node connect(Node root) {
Deque<Node> que = new LinkedList<>();
if (root == null) return null;
que.add(root);
while (!que.isEmpty()) {
int size = que.size();
while (size-- != 0) {
Node cur = que.poll();
if (size != 0) {
cur.next = que.peek();
}
if (cur.left != null) que.add(cur.left);
if (cur.right != null) que.add(cur.right);
}
}
return root;
}
}
117.填充每个节点的下一个右侧节点指针II
和上一题答案甚至都一样,这个感觉和完不完美二叉树没关系啊,反正是一层一层去看,没有左孩子只有右孩子又怎样。
104.二叉树的最大深度
也不难
class Solution {
public int maxDepth(TreeNode root) {
Deque<TreeNode> que = new LinkedList<>();
int res = 0;
if (root == null) return res;
que.add(root);
while (!que.isEmpty()) {
int size = que.size();
while (size-- != 0) {
TreeNode cur = que.poll();
if (cur.left != null) que.add(cur.left);
if (cur.right != null) que.add(cur.right);
}
res++;
}
return res;
}
}
111.二叉树的最小深度
这道题我没做对,按上一题最大深度的思路是不对的,应该是遇到了一个没有左右孩子的节点,就直接返回,他一定是最小深度的,去记录这个就行了。不需要设置MAX.VALUE 然后去比较这个,因为深度这个东西 都比他小啊。思路就错了
class Solution {
public int minDepth(TreeNode root) {
Deque<TreeNode> que = new LinkedList<>();
int level = 0;
if (root == null) return 0;
que.add(root);
while (!que.isEmpty()) {
int size = que.size();
level++;
while (size-- != 0) {
TreeNode cur = que.poll();
if (cur.left == null && cur.right == null) return level;
if (cur.left != null) que.add(cur.left);
if (cur.right != null) que.add(cur.right);
}
}
return level;
}
}
226.翻转二叉树 (优先掌握递归)
这道题目 一些做过的同学 理解的也不够深入,建议大家先看我的视频讲解,无论做过没做过,都会有很大收获。
题目链接/文章讲解/视频讲解:https://programmercarl.com/0226.%E7%BF%BB%E8%BD%AC%E4%BA%8C%E5%8F%89%E6%A0%91.html
第一印象:
第一眼感觉是不会的,还没有真正做过二叉树的应用的题目。直接看视频。
看完题解的思路:
思路不难,递归是我的弱项。返回值和终止条件比较好确认,逻辑就是交换两个孩子节点。
那么选择什么遍历方式就是关键了
这道题可以先序和后序,但中序很麻烦。因为中序遍历时,交换完左子树后,会把左右子树交换,再去交换右子树,但此时的右子树已经是刚才换过来的左子树了,就不对了。
实现时候的困难:
没有困难啊这。
感悟:
看来做二叉树的题,要先思考遍历树的方式。递归还是需要理解啊
代码:
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) return null;
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
invertTree(root.left);
invertTree(root.right);
return root;
}
}
101. 对称二叉树 (优先掌握递归)
先看视频讲解,会更容易一些。
题目链接/文章讲解/视频讲解:https://programmercarl.com/0101.%E5%AF%B9%E7%A7%B0%E4%BA%8C%E5%8F%89%E6%A0%91.html
第一印象:
第一想法是层序遍历然后用栈去收集结果,因为是对称的,2 3这样进去 出栈就是 3 2了,正好用于判断。想了一下就直接看视频了。
看完题解的思路:
我很难想到 对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。
那还是要先确认遍历顺序吧。
本题遍历只能是“后序遍历”,因为我们要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。
递归的时候
返回值:判断是不是对称的树,bool型
终止条件:判断左右两个节点是否相等,就会有很多情况。
- 左节点为空,右节点不为空,不对称,return false
- 左不为空,右为空,不对称 return false
- 左右都为空,对称,返回true
- 左右都不是空,数值不同,返回false
- 左右都不是空,数值相同,返回true
确定单层递归的逻辑:
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
实现中的困难:
思路都不清晰,实现直接抄题解理解一下。
感悟:
有点难理解
代码:
class Solution {
public boolean isSymmetric(TreeNode root) {
return compare(root.left, root.right);
}
private boolean compare(TreeNode left, TreeNode right){
if (left == null && right != null) return false;
if (left != null && right == null) return false;
if (left == null && right == null) return true;
if (left.val != right.val) return false;
boolean outside = compare(left.left, right.right);
boolean inside = compare(left.right, right.left);
return outside && inside;
}
}