# 二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

示例 1:

img

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

示例 2:

输入:root = [1]
输出:[[1]]

示例 3:

输入:root = []
输出:[]

提示:

  • 树中节点数目在范围 [0, 2000]
  • -1000 <= Node.val <= 1000

# 题目要求分析:

对二叉树进行一个层序遍历,顺序是一层一层的遍历

	   1
	 /   \
	2		 3
  /  \   /   \
 4		5 6	  7

从最顶层的数据节点也就是 1

1

然后就是第二层的节点

1	2	3

然后就是第三层,有四个数据节点

1	2	3	4	5	6	7

也就是说,我们遍历二叉树的顺序是按一层一层来的,将来遍历出来的应该就是 1, 2, 3, 4, 5, 6, 7。这叫层序遍历

# 思路分析:

	   1
	 /   \
	2		 3
  /  \   /   \
 4		5 6	  7
 
 # 我们可以设定一个队列
 头[]尾
 # 我们可以把第一层 根节点 加入到 队列里面去
 头[1]尾
 # 每次 从 队列 里 获取一个元素 
 1 # 把刚才的1 弹出,判断1是否有左右孩子,如果有比如说 左孩子2 那么就加入到队列里,右孩子3也加入到队列里面去
 头[2, 3]尾 # 此时队列里面存储了 第二层的数据
 # 此时 第一层的数据 已经遍历完了
 # 遍历第二层的数据,将队列 头部的 元素 弹出来
 1 2 # 把2 弹出,判断2是否有左右孩子,有就加入到队列里面
 头[3, 4, 5]尾 # 第三层的左半个节点就添加完了,重复这个过程
 1 2 3 # 把3 弹出, 判断是否有 左右孩子,有就加入到队列里面
 头[5, 6, 7]尾 # 第三层的右半个节点就添加完了,重复这个过程
 1 2 3 4 # 弹出4 判断是否有左右孩子,判断没有了,队列中就不需要加入节点了
 头[6, 7]尾
 1 2 3 4 5 # 弹出5 判断是否有左右孩子,判断没有了,队列中就不需要加入节点了
 头[7]尾
 1 2 3 4 5 6 # 弹出6 判断是否有左右孩子,判断没有了,队列中就不需要加入节点了
 头[]尾
 1 2 3 4 5 7 # 弹出7 判断是否有左右孩子,判断没有了,队列中就不需要加入节点了
  • 我们已知 第一个节点 是 root 节点 根节点,那么下面几层呢 都需要间接的获得

# 代码:

public List<List<Integer>> levelOrder(TreeNode root) {
      List<List<Integer>> res = new ArrayList<>();
      Queue<TreeNode> queue = new ArrayDeque<>();
      if(root != null)
      {
      queue.add(root);
      }
      int c1 = 1;
      while(! queue.isEmpty())
      {
        int c2 = 0;
        List<Integer> list = new ArrayList<>();
        for(int i = 0; i < c1; i++)
        {
          TreeNode node = queue.poll();
          list.add(node.val);
          if(node.left != null)
          {
            queue.add(node.left);
            c2++;
          }
          if(node.right != null)
          {
            queue.add(node.right);
            c2++;
          }
        }
        c1 = c2;
        res.add(list);
      }
      return res;
    }

# leetcode 中 精选 题解 分享

leetcode 中 精选 的 解题 分享:

分享地址 https://leetcode.cn/problems/binary-tree-level-order-traversal/solutions/244853/bfs-de-shi-yong-chang-jing-zong-jie-ceng-xu-bian-l/

# 解题思路

本文将会讲解为什么这道题适合用广度优先搜索(BFS),以及 BFS 适用于什么样的场景。

DFS(深度优先搜索)和 BFS(广度优先搜索)就像孪生兄弟,提到一个总是想起另一个。然而在实际使用中,我们用 DFS 的时候远远多于 BFS。那么,是不是 BFS 就没有什么用呢?

如果我们使用 DFS/BFS 只是为了遍历一棵树、一张图上的所有结点的话,那么 DFS 和 BFS 的能力没什么差别,我们当然更倾向于更方便写、空间复杂度更低的 DFS 遍历。不过,某些使用场景是 DFS 做不到的,只能使用 BFS 遍历。这就是本文要介绍的两个场景:「层序遍历」、「最短路径」。

本文包括以下内容:

DFS 与 BFS 的特点比较
BFS 的适用场景
如何用 BFS 进行层序遍历
如何用 BFS 求解最短路径问题
DFS 与 BFS
让我们先看看在二叉树上进行 DFS 遍历和 BFS 遍历的代码比较。

DFS 遍历使用 递归:

void dfs(TreeNode root) {
    if (root == null) {
        return;
    }
    dfs(root.left);
    dfs(root.right);
}

BFS 遍历使用队列数据结构:

void bfs(TreeNode root) {
    Queue<TreeNode> queue = new ArrayDeque<>();
    queue.add(root);
    while (!queue.isEmpty()) {
        TreeNode node = queue.poll(); // Java 的 pop 写作 poll ()
        if (node.left != null) {
            queue.add(node.left);
        }
        if (node.right != null) {
            queue.add(node.right);
        }
    }
}

只是比较两段代码的话,最直观的感受就是:DFS 遍历的代码比 BFS 简洁太多了!这是因为递归的方式隐含地使用了系统的 栈,我们不需要自己维护一个数据结构。如果只是简单地将二叉树遍历一遍,那么 DFS 显然是更方便的选择。

虽然 DFS 与 BFS 都是将二叉树的所有结点遍历了一遍,但它们遍历结点的顺序不同

DFS 与 BFS 对比

这个遍历顺序也是 BFS 能够用来解「层序遍历」、「最短路径」问题的根本原因。下面,我们结合几道例题来讲讲 BFS 是如何求解层序遍历和最短路径问题的。

BFS 的应用一:层序遍历
BFS 的层序遍历应用就是本题了:

LeetCode 102. Binary Tree Level Order Traversal 二叉树的层序遍历(Medium)

给定一个二叉树,返回其按层序遍历得到的节点值。 层序遍历即逐层地、从左到右访问所有结点。

什么是层序遍历呢?简单来说,层序遍历就是把二叉树分层,然后每一层从左到右遍历

二叉树的层序遍历

乍一看来,这个遍历顺序和 BFS 是一样的,我们可以直接用 BFS 得出层序遍历结果。然而,层序遍历要求的输入结果和 BFS 是不同的。层序遍历要求我们区分每一层,也就是返回一个二维数组。而 BFS 的遍历结果是一个一维数组,无法区分每一层。

BFS 遍历与层序遍历的输出结果不同

那么,怎么给 BFS 遍历的结果分层呢?我们首先来观察一下 BFS 遍历的过程中,结点进队列和出队列的过程:

BFS 遍历的过程(动图)

截取 BFS 遍历过程中的某个时刻

BFS 遍历中某个时刻队列的状态

可以看到,此时队列中的结点是 3、4、5,分别来自第 1 层和第 2 层。这个时候,第 1 层的结点还没出完,第 2 层的结点就进来了,而且两层的结点在队列中紧挨在一起,我们 无法区分队列中的结点来自哪一层。

因此,我们需要稍微修改一下代码,在每一层遍历开始前,先记录队列中的结点数量 n(也就是这一层的结点数量),然后一口气处理完这一层的 n 个结点

// 二叉树的层序遍历
void bfs(TreeNode root) {
    Queue<TreeNode> queue = new ArrayDeque<>();
    queue.add(root);
    while (!queue.isEmpty()) {
        int n = queue.size();
        for (int i = 0; i < n; i++) { 
            // 变量 i 无实际意义,只是为了循环 n 次
            TreeNode node = queue.poll();
            if (node.left != null) {
                queue.add(node.left);
            }
            if (node.right != null) {
                queue.add(node.right);
            }
        }
    }
}

这样,我们就将 BFS 遍历改造成了层序遍历。在遍历的过程中,结点进队列和出队列的过程为

img

可以看到,在 while 循环的每一轮中,都是将当前层的所有结点出队列,再将下一层的所有结点入队列,这样就实现了层序遍历

最终我们得到的题解代码为:

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> res = new ArrayList<>();
    Queue<TreeNode> queue = new ArrayDeque<>();
    if (root != null) {
        queue.add(root);
    }
    while (!queue.isEmpty()) {
        int n = queue.size();
        List<Integer> level = new ArrayList<>();
        for (int i = 0; i < n; i++) { 
            TreeNode node = queue.poll();
            level.add(node.val);
            if (node.left != null) {
                queue.add(node.left);
            }
            if (node.right != null) {
                queue.add(node.right);
            }
        }
        res.add(level);
    }
    return res;
}