层序遍历看完本篇可以一口气刷十道题,试一试, 层序遍历并不难,大家可以很快刷了十道题。
题目链接/文章讲解/视频讲解:代码随想录
层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。
需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。
在弹出上一层的所有元素后,需要更新此时队列大小(当前层元素个数),接下来遍历队列时,只弹出这一层的所有元素(因为队列里的元素数量是不断变化的,提前记录才知道需要弹出多少元素)
弹出一个元素后,需要将它的左右孩子加入队列
102.二叉树的层序遍历
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
/**
* 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 List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
Deque<TreeNode> dequeue = new LinkedList<>();
if (root == null) return result;
dequeue.offer(root);
while(!dequeue.isEmpty()) {//遍历一层
int size = dequeue.size();// 本层节点数量
List<Integer> res = new ArrayList<>();
while(size-- > 0) {//遍历层中每个元素
TreeNode node = dequeue.poll();
res.add(node.val);
if(node.left != null) dequeue.offer(node.left);
if(node.right != null) dequeue.offer(node.right);
}
result.add(res);
}
return result;
}
}
107.二叉树的层次遍历 II
给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
思路:相对于102.二叉树的层序遍历,就是最后把result数组反转一下就可以了。
199.二叉树的右视图
给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
思路:层序遍历的时候,判断是否遍历到单层的最后面的元素,如果是,就放进result数组中,随后返回result就可以了。
637.二叉树的层平均值
给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。
思路: 本题就是层序遍历的时候把一层求个总和在取一个均值。(不能最后强转sum,而应该在一开始定义时就将sum定义给double,防止溢出)
429.N叉树的层序遍历
给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。
思路: 这道题依旧是模板题,只不过一个节点有多个孩子了
515.在每个树行中找最大值
您需要在二叉树的每一行中找到最大的值。
思路:层序遍历,取每一层的最大值(max应该初始化为Integer最小值,而非0)
116.填充每个节点的下一个右侧节点指针
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
思路: 本题依然是层序遍历,只不过在单层遍历的时候记录一下本层的头部节点,然后在遍历的时候让前一个节点指向本节点就可以了
117.填充每个节点的下一个右侧节点指针II
思路:这道题目说是二叉树,但116题目说是完整二叉树,其实没有任何差别,一样的代码一样的逻辑一样的味道
104.二叉树的最大深度
给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。说明: 叶子节点是指没有子节点的节点。
思路:(注意统计深度的位置)使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。
在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度。所以这道题的迭代法就是一道模板题,可以使用二叉树层序遍历的模板来解决的。
111.二叉树的最小深度
相对于 104.二叉树的最大深度 ,本题还也可以使用层序遍历的方式来解决,思路是一样的。
(注意统计深度的位置)需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点
226.翻转二叉树 (优先掌握递归)这道题目 一些做过的同学 理解的也不够深入,建议大家先看我的视频讲解,无论做过没做过,都会有很大收获。
题目链接/文章讲解/视频讲解:代码随想录
想要翻转它,其实就把每一个节点的左右孩子交换一下就可以了。
遍历的过程中去翻转每一个节点的左右孩子就可以达到整体翻转的效果。
注意只要把每一个节点的左右孩子翻转一下,就可以达到整体翻转的效果
这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次!
(中序遍历:先交换处理完左子树,处理中间节点把左右子树调换,又去交换处理右子树(之前的左子树),导致一边子树处理两次,一边子树完全没处理过)所以需要继续处理左子树,而非右子树(不过不直观,容易绕进去)
但使用迭代方式统一写法的中序是可以的。因为这是用栈来遍历,而不是靠指针来遍历,避免了递归法中翻转了两次的情况
层序遍历也是可以的!只要把每一个节点的左右孩子翻转一下的遍历方式都是可以的!
/**
* 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;
* }
* }
*/
//DFS
class Solution {
//确定递归函数的参数和返回值
public TreeNode invertTree(TreeNode root) {
//确定终止条件
if(root == null) return root;
//确定单层递归的逻辑
// 中
TreeNode temp;
temp = root.left;
root.left = root.right;
root.right = temp;
invertTree(root.left);// 左
invertTree(root.right);// 右 如果是中序遍历,得要是invertTree(root.left);
return root;
}
}
//BFS
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) {return null;}
ArrayDeque<TreeNode> deque = new ArrayDeque<>();
deque.offer(root);
while (!deque.isEmpty()) {
int size = deque.size();
while (size-- > 0) {
TreeNode node = deque.poll();
swap(node);
if (node.left != null) deque.offer(node.left);
if (node.right != null) deque.offer(node.right);
}
}
return root;
}
public void swap(TreeNode root) {
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
}
}
101. 对称二叉树 (优先掌握递归)先看视频讲解,会更容易一些。
题目链接/文章讲解/视频讲解:代码随想录
关键:
- 判断对称二叉树就是判断左右孩子是否可以翻转
- 理解遍历顺序
本题遍历只能是“后序遍历”,因为我们要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。
正是因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。
但都可以理解算是后序遍历,尽管已经不是严格上在一个树上进行遍历的后序遍历了。
需要收集子树信息,向上一层返回的这一类题目,都需要后序遍历
考察的是同时处理两个二叉树的遍历过程(同时比较两个二叉树节点的对应情况)
/**
* 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 boolean isSymmetric(TreeNode root) {
return compare(root.left, root.right);
}
public boolean compare(TreeNode left, TreeNode right){
if(left == null && right != null){//左空右不空
return false;
}else if(left != null && right == null){//左不空右空
return false;
}else if(left == null && right == null){//左空右空
return true;
}else if(left.val != right.val){//左右节点不为空,且数值不相等
return false;
}
// 左右节点不为空,且数值相等,向下一层遍历,左子树左右中,右子树右左中,都是后序遍历
boolean outside = compare(left.left, right.right);//左
boolean inside = compare(left.right, right.left);//右
boolean result = outside && inside;//中,依赖于外侧和内测的比较情况,所以前序和中序不行
return result;
}
}
迭代法:
使用队列来比较两个树(根节点的左右子树)是否相互翻转,(注意这不是层序遍历)
通过队列来判断根节点的左子树和右子树的内侧和外侧是否相等:
- 将左右子树头结点同时加入队列
- 判断两个子树是否相互翻转
在迭代法中我们使用了队列,需要注意的是这不是层序遍历,而且仅仅通过一个容器来成对的存放我们要比较的元素,知道这一本质之后就发现,用队列,用栈,甚至用数组,都是可以的。