一.二叉树层序遍历
1.介绍
层序遍历类似于广度优先搜索,它按照二叉树的层次从上到下、从左到右遍历二叉树的每个节点。 每次遍历一层元素,借助队列实现, 首先叶子节点入队,然后左节点不为空时入队,右节点不为空时入队, 每次扩张当前层元素,size统计当前元素个数,然后挨个扩张,将该结点的左右孩子加入队列
2.代码实现
层序遍历就是一层一层处理元素,每次遍历每层size个,对于每层的每个元素,加入集合,然后扩展左右孩子入队作为下一层元素,本遍历完后更新size开始下一层
首先根结点入队,第一层个数就是1,记下size,对第一层处理,只处理前size个,加入数组,拓展左右孩子入队,然后更新size,处理第二层,加入数组,拓展左右孩子入队
1.队列方式
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> resList = new ArrayList<>();//结果集合
if (root == null) return resList;
Queue<TreeNode> queue = new LinkedList<>();//创建对列
queue.offer(root);//根结点入队
while (!queue.isEmpty()) {//队不为空时
int len = queue.size(); //遍历本层元素size个
// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
List<Integer> tempList = new ArrayList<>();
while (len > 0) {//遍历本层元素size个
TreeNode tempNode = queue.poll();
tempList.add(tempNode.val);//数值加入当前层结果中
if (tempNode.left != null) queue.offer(tempNode.left);//左孩子入队
if (tempNode.right != null) queue.offer(tempNode.right);//右孩子入队
len--;
}
resList.add(tempList);//当前层结果加入最终结果
}
return resList;
}
}
2.递归方式 dfs
在Java中,递归方式实现二叉树的层序遍历并不常见,因为层序遍历(广度优先遍历)通常使用队列来实现。然而,如果使用递归来实现类似层序遍历的效果(虽然这并不是真正的层序遍历,而是深度优先遍历),可能需要实现一个类似的前序遍历,并在遍历的过程中记录每个节点的层级。
通过传递层级信息deep
和使用一个列表的列表resList
来存储每一层的节点值,模拟了类似层序遍历的输出结构。
import java.util.ArrayList;
import java.util.List;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}
public class BinaryTreeTraversal {
// 用于存储结果的列表的列表
private List<List<Integer>> resList = new ArrayList<>();
// 递归函数,模拟类似层序遍历的效果
public void checkFun01(TreeNode node, Integer deep) {
if (node == null) return; // 如果节点为空,则直接返回
deep++; // 层级加1
// 当结果列表的size小于当前层级时,添加一个新的列表来存储该层级的节点值
if (resList.size() < deep) {
List<Integer> item = new ArrayList<>();
resList.add(item);
}
// 将当前节点的值添加到对应层级的列表中
resList.get(deep - 1).add(node.val);
// 递归遍历左子树,层级信息不变(因为是同一层级的不同分支)
checkFun01(node.left, deep);
// 递归遍历右子树,层级信息不变(因为是同一层级的不同分支)
checkFun01(node.right, deep);
}
// 测试方法
public static void main(String[] args) {
BinaryTreeTraversal traversal = new BinaryTreeTraversal();
// 创建一个二叉树
// 1
// / \
// 2 3
// / \ \
// 4 5 6
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
root.right.right = new TreeNode(6);
// 调用递归方法
traversal.checkFun01(root, 1); // 从层级1开始
// 打印结果
for (List<Integer> level : traversal.resList) {
System.out.println(level);
}
}
}
二.练习扩展
1.二叉树的层序遍历2
思路
相对于102.二叉树的层序遍历,就是最后把result数组反转一下就可以了。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> res=new ArrayList<>();
List<List<Integer>> res1=new ArrayList<>();
Queue<TreeNode> queue=new LinkedList<>();
//层序遍历二叉树
if(root==null){
return res;
}
queue.offer(root);
while (!queue.isEmpty()){
List<Integer> list=new ArrayList<>();
int size=queue.size();
for (int i = 0; i < size; i++) {
TreeNode poll = queue.poll();
list.add(poll.val);
if(poll.left!=null){
queue.offer(poll.left);
}
if(poll.right!=null){
queue.offer(poll.right);
}
}
res.add(list);
}
for (int i = res.size()-1; i>=0;i--) {
res1.add(res.get(i));
}
return res1;
}
}
2.二叉树的右视图
思路
层序遍历的时候,遍历到单层的最后面的元素放进result数组中,随后返回result就可以了。
class Solution {
// 声明一个队列用于层序遍历
Queue<TreeNode> queue = new LinkedList<>();
public List<Integer> rightSideView(TreeNode root) {
// 创建一个列表用于存储每一层最右侧的节点值
List<Integer> res = new ArrayList<>();
// 如果根节点为空,则直接返回空列表
if (root == null) {
return res;
}
// 将根节点加入队列
queue.offer(root);
// 当队列不为空时,进行层序遍历
while (!queue.isEmpty()) {
// 创建一个临时列表用于存储当前层的节点值
List<Integer> list = new ArrayList<>();
// 获取当前层的节点数量
int size = queue.size();
// 遍历当前层的所有节点
while (size > 0) {
// 出队一个节点,并将其值加入临时列表
TreeNode poll = queue.poll();
list.add(poll.val);
// 如果左子节点不为空,则将其加入队列
if (poll.left != null) {
queue.offer(poll.left);
}
// 如果右子节点不为空,则将其加入队列
if (poll.right != null) {
queue.offer(poll.right);
}
// 当前层遍历的节点数减一
size--;
}
// 将当前层最右侧的节点值(即临时列表的最后一个元素)加入结果列表
res.add(list.get(list.size() - 1));
}
// 返回结果列表
return res;
}
}
// 假设TreeNode类已经定义如下:
// class TreeNode {
// int val;
// TreeNode left;
// TreeNode right;
// TreeNode(int x) { val = x; }
// }
3.二叉树的层平均值
思路
层序遍历的时候把一层求个总和在取一个均值。
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
// 创建一个列表用于存储每一层的平均值
List<Double> avg = new ArrayList<>();
// 创建一个队列用于层序遍历
Queue<TreeNode> queue = new LinkedList<>();
// 将根节点加入队列
if (root != null) {
queue.offer(root);
}
// 当队列不为空时,说明还有节点需要遍历
while (!queue.isEmpty()) {
// 初始化当前层的和
double sum = 0;
// 获取当前层的节点数量
int size = queue.size();
// 遍历当前层的所有节点
for (int i = 0; i < size; i++) {
// 出队一个节点
TreeNode node = queue.poll();
// 累加当前节点的值
sum += node.val;
// 如果左子节点不为空,将其加入队列
if (node.left != null) {
queue.offer(node.left);
}
// 如果右子节点不为空,将其加入队列
if (node.right != null) {
queue.offer(node.right);
}
}
// 计算当前层的平均值,并添加到结果列表中
avg.add(sum / size);
}
// 返回每一层的平均值列表
return avg;
}
}
4.二叉树的最大深度
深度 某个节点到根节点距离从1开始计算
高度 某节点到叶子节点距离 从1开始计算
思路
使用层序遍历是最合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。
在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度
class Solution {
// 计算二叉树的最大深度
public int maxDepth(TreeNode root) {
// 如果根节点为空,则树的深度为0
if (root == null) {
return 0;
}
// 使用队列进行层序遍历
Queue<TreeNode> que = new LinkedList<>();
// 将根节点加入队列
que.offer(root);
// 初始化深度为1(因为已经有一个根节点了)
int depth = 1;
// 当队列不为空时,说明还有节点需要遍历
while (!que.isEmpty()) {
// 当前层的节点数量
int len = que.size();
// 遍历当前层的所有节点
while (len > 0) {
// 出队一个节点
TreeNode node = que.poll();
// 如果节点的左子节点不为空,则将左子节点加入队列
if (node.left != null) {
que.offer(node.left);
}
// 如果节点的右子节点不为空,则将右子节点加入队列
if (node.right != null) {
que.offer(node.right);
}
// 遍历完一个节点后,当前层的节点数量减一
len--;
}
// 完成当前层的遍历后,深度加1,因为已经遍历到了下一层
depth++;
}
// 返回二叉树的最大深度
return depth;
}
}
5.二叉树的最小深度
思路
相对于二叉树的最大深度 ,本题也可以使用层序遍历的方式来解决,思路是一样的。
需要注意,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点
class Solution {
public int minDepth(TreeNode root){
if (root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int depth = 0;
while (!queue.isEmpty()){
int size = queue.size();
depth++;
TreeNode cur = null;
for (int i = 0; i < size; i++) {
cur = queue.poll();
//如果当前节点的左右孩子都为空,直接返回最小深度
if (cur.left == null && cur.right == null){
return depth;
}
if (cur.left != null) queue.offer(cur.left);
if (cur.right != null) queue.offer(cur.right);
}
}
return depth;
}
}