Pattern: Tree Breadth First Search,树上的BFS

介绍来自链接:https://www.zhihu.com/question/36738189/answer/908664455 作者:穷码农

​ 这种模式基于宽度优先搜索(Breadth First Search (BFS)),适用于需要遍历一颗树。借助于队列数据结构,从而能保证树的节点按照他们的层数打印出来。打印完当前层所有元素,才能执行到下一层。所有这种需要遍历树且需要一层一层遍历的问题,都能用这种模式高效解决。

​ 这种树上的BFS模式是通过把根节点加到队列中,然后不断遍历直到队列为空。每一次循环中,我们都会把队头结点拿出来(remove),然后对其进行必要的操作。在删除每个节点的同时,其孩子节点,都会被加到队列中。

识别树上的BFS模式:

  • 如果你被问到去遍历树,需要按层操作的方式(也称作层序遍历)

labuladong的算法小抄 上的算法框架,值得看看:

// 计算从起点 start 到终点 target 的最近距离
int BFS(Node start, Node target) {Queue<Node> q;      // 核心数据结构:队列Set<Node> visited;    // 避免走回头路q.offer(start);    // 将起点加入队列visited.add(start);int step = 0;         // 记录扩散的步数while (q not empty) {int sz = q.size();/* 将当前队列中的所有节点向四周扩散 */for (int i = 0; i < sz; i++) {Node cur = q.poll();/* 划重点:这里判断是否到达终点 */if (cur is target)return step;/* 将 cur 的相邻节点加入队列 */for (Node x : cur.adj())if (x not in visited) {q.offer(x);visited.add(x);}}/* 划重点:更新步数在这里 */step++;}
}

队列 q 就不说了,BFS 的核心数据结构;cur.adj() 泛指 cur 相邻的节点,比如说二维数组中,cur 上下左右四面的位置就是相邻节点;visited 的主要作用是防止走回头路,大部分时候都是必须的,但是像一般的二叉树结构,没有子节点到父节点的指针,不会走回头路就不需要 visited


可以先去了解一下宽度优先搜索算法,再做题。

队列的四组API

方式 抛出异常 有返回值,不抛出异常 阻塞 等待 超时等待
添加 add offer() put() offer(,)
移除 remove poll() take() poll(,)
检测队首元素 element peek
方法 功能 返回值
add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
offer 添加一个元素并返回true 如果队列已满,则返回false
poll 移除并返问队列头部的元素 如果队列为空,则返回null
peek 返回队列头部的元素 如果队列为空,则返回null
put 添加一个元素 如果队列满,则阻塞
take 移除并返回队列头部的元素 如果队列为空,则阻塞

经典题目:

1、Binary Tree Level Order Traversal (easy)

102. 二叉树的层序遍历

描述:

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

示例:
二叉树:[3,9,20,null,null,15,7],

 3/ \9  20/  \15   7

​ 返回其层次遍历结果:

[[3],[9,20],[15,7]
]

解题思路: 层次遍历和按照顺序首先要想到的是队列数据结构。

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode(int x) { val = x; }* }*/
class Solution {public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> res = new ArrayList<>();if (root == null)return res;Queue<TreeNode> queue = new LinkedList<>();     // 用 LinkedList 实现队列数据结构queue.offer(root);      // 根节点入列while (!queue.isEmpty()){       // 队列不为空则遍历List<Integer> levelList = new ArrayList<>();      // 每一层的结果存放int level = queue.size();       // 每层的节点数for (int i = 0; i < level; i++) {           // 保存下一层到队列中if (queue.peek().left != null) {queue.offer(queue.peek().left);     // 将队头 左节点 入列}if (queue.peek().right != null) {queue.offer(queue.peek().right);    // 将队头 右节点 入列}levelList.add(queue.poll().val);        // 节点出列, 保存节点的值}res.add(levelList);}return res;}
}

2、Reverse Level Order Traversal (easy)

70. 二叉树的层次遍历 II

107. 二叉树的层次遍历 II

描述:

​ 给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

例如:

​ 给定二叉树 [3,9,20,null,null,15,7],

   3/ \9  20/  \15   7

​ 返回其自底向上的层次遍历为:

[[15,7],[9,20],[3]
]

**解题思路:**上一题中的结果反转,可以将下一层的结果保存到结果集的首部。从头插入使用 LinkedList 实现效率比较高,底层实现是双向链表。ArrayList 底层实现是 Object 数组,使用头插的话要挪动所有的元素,效率低。

class Solution {public List<List<Integer>> levelOrderBottom(TreeNode root) {LinkedList<List<Integer>> res = new LinkedList<>();if (root == null)return res;Queue<TreeNode> queue = new LinkedList<>();     // 用 LinkedList 实现队列数据结构queue.offer(root);      // 根节点入列while (!queue.isEmpty()){       // 队列不为空则遍历int level = queue.size();       // 每层的节点数List<Integer> levelList = new ArrayList<>(level);      // 每一层的结果存放for (int i = 0; i < level; i++) {           // 保存下一层到队列中if (queue.peek().left != null) {queue.offer(queue.peek().left);     // 将队头 左节点 入列}if (queue.peek().right != null) {queue.offer(queue.peek().right);    // 将队头 右节点 入列}levelList.add(queue.poll().val);        // 节点出列, 保存节点的值}res.addFirst(levelList);    // 下一层加入到结果的头部}return res;}
}

3、Zigzag Traversal (medium)

103. 二叉树的锯齿形层次遍历

描述:

​ 给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

例如:

给定二叉树 [3,9,20,null,null,15,7],

 3/ \9  20/  \15   7

返回锯齿形层次遍历如下:

[[3],[20,9],[15,7]
]

解题思路: 记录遍历的层数,偶数层的结果记录使用尾插,奇数层的结果记录使用头插。

class Solution {public List<List<Integer>> zigzagLevelOrder(TreeNode root) {LinkedList<List<Integer>> res = new LinkedList<>();if (root == null)return res;Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);int level = 0;      // 记录层数while (!queue.isEmpty()){LinkedList<Integer> list = new LinkedList<>();int size = queue.size();for (int i = 0; i < size; i++) {if (queue.peek().left != null){queue.offer(queue.peek().left);}if (queue.peek().right != null){queue.offer(queue.peek().right);}if (level % 2 == 0){                // 偶数层顺序插入list.add(queue.poll().val);}else {                             // 奇数层倒序插入list.addFirst(queue.poll().val);}}level++;         // 层数 + 1res.add(list);}return res;}
}

4、Level Averages in a Binary Tree (easy)

637. 二叉树的层平均值

描述:

​ 给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。

示例 1:

输入:3/ \9  20/  \15   7
输出:[3, 14.5, 11]
解释:
第 0 层的平均值是 3 ,  第1层是 14.5 , 第2层是 11 。因此返回 [3, 14.5, 11] 。

解题思路: 记录每一层节点总和 / 每一层节点数

class Solution {public List<Double> averageOfLevels(TreeNode root) {List<Double> res = new ArrayList<>();if (root == null)return res;Queue<TreeNode> queue = new LinkedList<>();     // 队列queue.offer(root);                              // 头结点入列while(!queue.isEmpty()){int size = queue.size();                    // 每层节点数double sum = 0;                             // 每层和for (int i = 0; i < size; i++) {if (queue.peek().left != null){queue.offer(queue.peek().left);     // 下层左节点}if (queue.peek().right != null){queue.offer(queue.peek().right);    // 下层右节点}sum += queue.poll().val;                // 计算总和}res.add(sum / size);                        // 保存平均值}return res;}
}

5、Minimum Depth of a Binary Tree (easy)

111. 二叉树的最小深度

描述:

​ 给定一个二叉树,找出其最小深度。

​ 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

​ **说明:**叶子节点是指没有子节点的节点。

示例 1:

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

示例 2:

输入:root = [2,null,3,null,4,null,5,null,6]
输出:5

解题思路:

1、 使用宽度优先搜索,处理每层的节点时,如果有节点既没有左节点和右节点时即达到了叶子节点,直接返回该层的深度。处理完一层的节点后深度+1

class Solution {public int minDepth(TreeNode root) {if (root == null)return 0;Queue<TreeNode> queue = new LinkedList<>();     // 队列queue.offer(root);                              // 头结点入列int depth = 1;      // 记录深度while(!queue.isEmpty()){int size = queue.size();                    // 每层节点数double sum = 0;                             // 每层和for (int i = 0; i < size; i++) {            // 处理一层的节点if (queue.peek().left != null){queue.offer(queue.peek().left);     // 下层左节点}if (queue.peek().right != null){queue.offer(queue.peek().right);    // 下层右节点}// 该节点为叶子节点,直接返回深度if (queue.peek().left == null && queue.peek().right == null){return depth;}// 该节点出列queue.poll();   }depth++;    // 一层处理完后,深度 +1}return depth;}
}

2、使用递归:深度优先搜索。

思路:

  • 叶子节点的定义是左孩子和右孩子都为 null 时叫做叶子节点
  • 当 root 节点左右孩子都为空时,返回 1
  • 当 root 节点左右孩子有一个为空时,返回不为空的孩子节点的深度
  • 当 root 节点左右孩子都不为空时,返回左右孩子较小深度的节点值

class Solution {public int minDepth(TreeNode root) {if (root == null)return 0;int left = minDepth(root.left);int right = minDepth(root.right);//1.如果左孩子和右孩子有为空的情况,说明left和right 必然有一个为 0。直接返回 left + right + 1//2.如果都不为空,返回较小深度+1return root.left == null || root.right == null ? left + right + 1 : Math.min(left, right) + 1;}
}

6、Level Order Successor (easy)

面试题 04.06. 后继者

448. 二叉查找树的中序后继

描述

​ 给定一个二叉查找树,以及一个节点,求该节点在中序遍历的后继,如果没有则返回null

​ 保证p是给定二叉树中的一个节点。(您可以直接通过内存地址找到p)

样例

样例 1:

输入: {1,#,2}, node with value 1
输出: 2
解释: 1\2中序遍历结果:[1, 2]

样例 2:

输入: {2,1,3}, node with value 1
输出: 2
解释: 2/ \1   3中序遍历结果:[1, 2, 3]

样例 3:

输入: root = [5,3,6,2,4,null,null,1], p = 6
输出: null5/ \3   6/ \2   4/
1中序遍历结果:[1, 2, 3, 4, 5, 6]
1、先序遍历二叉树:

二叉树为空,则空操作。否则

​ (1)访问根节点

​ (2)先序遍历左子树

​ (3)先序遍历右子树

2、中序遍历二叉树:

二叉树为空,则空操作。否则

​ (1)中序遍历左子树

​ (2)访问根节点

​ (3)中序遍历右子树

3、后序遍历二叉树:

二叉树为空,则空操作。否则

​ (1)后序遍历左子树

​ (2)后序遍历右子树

​ (3)访问根节点

一棵二叉查找树(BST)定义为:
  • 节点的左子树中的值要严格小于该节点的值。
  • 节点的右子树中的值要严格大于该节点的值。
  • 左右子树也必须是二叉查找树。
  • 一个节点的树也是二叉查找树。

节点的祖先是从根节点到该节点所经分支上的所有节点。反之,以某个节点为根的子树中的任一结点都称为该节点的子孙

遍历二叉树是以一定的规则将二叉树中的节点排成一个线性的序列,得到二叉树中的节点的先序序列中序序列后序序列。这实质上是对一个非线性结构进行线性化操作,使得每个节点(除第一个和最后一个外)在这些线性序列中有且仅有一个直接前驱和直接后继。例如下图中序序列为 DBHEAFCIG , A的前驱为E,后继为F

中序遍历的后继:

  • 当前节点存在右子树:那么当前节点的后继为右子节点的子树中最左端的节点。例如B存在右子树,则其后继为右子节点E的最左端节点H
  • 当前节点不存在右子树:

淦!卡了好几天不理解,先跳过,以后有时间再回来解决吧。

7、Connect Level Order Siblings (medium)

429. N 叉树的层序遍历

描述:

​ 给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。

​ 树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。

示例:

示例 1:

输入:root = [1,null,3,2,4,null,5,6]
输出:[[1],[3,2,4],[5,6]]

示例 2:

输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
输出:[[1],[2,3,4,5],[6,7,8,9,10],[11,12,13],[14]]

解题思路: 宽度优先搜索+队列

/*
// Definition for a Node.
class Node {public int val;public List<Node> children;public Node() {}public Node(int _val) {val = _val;}public Node(int _val, List<Node> _children) {val = _val;children = _children;}
};
*/class Solution {public List<List<Integer>> levelOrder(Node root) {List<List<Integer>> res = new ArrayList<>();if (root == null)return res;Queue<Node> queue = new LinkedList<>();     // 队列queue.offer(root);                          // 头结点入列while (!queue.isEmpty()){List<Integer> levelList = new ArrayList<>();        // 每一层的结果保存int size = queue.size();                            // 每一层的节点个数for (int i = 0; i < size; i++) {                    // 处理一层的节点queue.addAll(queue.peek().children);            // 保存下一层的节点levelList.add(queue.poll().val);                // 保存完下一层节点,节点出列并保存其值}res.add(levelList);     // 保存每一层的结果}return res;}
}

树上的BFS(Tree Breadth First Search)相关推荐

  1. DFS(Depth First Search,深度优先搜索)与BFS(Breadth First Search,广度优先搜索)总结与思考

    目录 Depth First Search(dfs) 代码(递归) 代码(非递归) Breadth First Search(bfs) 代码 比较 数据结构 时间复杂度 使用 例题:岛的个数 dfs实 ...

  2. [Tree Breadth First Search] 二叉树的最大深度

    leetcode 104.Maximum Depth of Binary Tree.难度easy 0. 题干 给定一个二叉树,找出其最大深度. 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数. ...

  3. 数据结构与算法(python):广度优先搜索(Breadth First Search,BFS)和深度优先算法(Depth First Search,DFS)

    参考自 MOOC数据结构与算法Python版 目录 一.广度优先搜索 1.1 BFS算法过程 1.2 广度优先搜索算法分析 二.深度优先搜索 2.1 骑士周游 2.1.1 问题定义 2.1.2 构建骑 ...

  4. JavaScript实现breadth First Search广度优先搜索算法(附完整源码))

    JavaScript实现breadth First Search广度优先搜索算法(附完整源码) LinkedListNode.js完整源代码 Comparator.js完整源代码 LinkedList ...

  5. C++Breadth First Search 广度优先搜索(附完整源码)

    C++Breadth First Search 广度优先搜索的实现 C++Breadth First Search 广度优先搜索实现完整源码(定义,实现,main函数测试) C++Breadth Fi ...

  6. BFS——广度优先算法(Breadth First Search)

    1.前言 这几天刷leetcode经常碰到DFS BFS的问题,之前一直也是模棱两可,凭着感觉做,是需要总结一下了. 广度优先搜索(也称宽度优先搜索,缩写BFS,以下采用广度来描述)是连通图的一种遍历 ...

  7. 图的遍历——广度优先搜索(Breadth First Search)

    2019独角兽企业重金招聘Python工程师标准>>> 1.广度优先搜索遍历类似于树的按层次遍历的过程. 假设从图中某顶点V出发,在访问了V之后依次访问V的各个未曾访问过得邻接点,然 ...

  8. 深度优先搜索(Depth First Search)、广度优先搜索(Breadth First Search)

    DFS: /* 邻接表存储的图 - DFS */void Visit( Vertex V ) {printf("正在访问顶点%d\n", V); }/* Visited[]为全局变 ...

  9. 广度优先搜索(Breadth First Search)

    广度优先搜索简称BFS,是一种以"广度"为第一关键词的算法,当碰到岔道口时,总是优先考虑从该岔道口能直接到达的所有节点,以此类推,直到所有节点都被访问位置,类似于掉入水面的石子,激 ...

最新文章

  1. 南华大学计算机学院足球队,球场健儿,不言放弃——记南华大学“新生杯”足球赛...
  2. Keras【Deep Learning With Python】LSTM 循环神经网络解决Regressor回归问题
  3. android getview方法,android 获取view的getLeft(), getRight(), getTop(),... - 简书
  4. ios开发闹钟源代码_开源源码让短视频的开发变得更加便捷
  5. 汇编---输出AX的地址值
  6. Android极光推送,Manifest merger failed with multiple errors, see logs
  7. Prim和Kruskal求最小生成树
  8. MyBatis学习总结_Mybatis查询之resultMap和resultType区别
  9. 牛客练习-哈尔滨理工大学21级新生程序设计竞赛(同步赛)
  10. watch 与 computed
  11. 【小聪明】图片消失在另一张图片里
  12. 应用密码学:协议、算法与C源程序(学习第一章)
  13. STM32入门——什么是STM32
  14. 第六章:组合数据类型练习[人名独特性统计]学习思考
  15. centos8安装NVIDIA显卡驱动,docker模式运行机器学习
  16. 反向代理为何叫反向代理?
  17. python与大数据
  18. 使用handeye_calib_camodocal进行手眼标定
  19. 微软计算机电源怎么接,这个长得像电源插头的东西,真的是微软最新研发的迷你电脑?!...
  20. week after week----友情岁月,匆匆

热门文章

  1. c语言乐学编程作业答案,信息乐学|浅谈C语言
  2. Linux 判断文件或文件夹是否存在
  3. matlab打包训练好的神经网络(BP神经网络)
  4. 零基础转行网络安全真的好就业吗?
  5. SuperMap iClient3D for WebGL教程 粒子特效-基础火焰特效
  6. 红米 刷机 android7.1,红米Note5A高配稳定版固件刷机包:V11.0.1.0 安卓7.1
  7. python3数据分析的书籍_从零开始用Python3做数据分析
  8. 关于对《三只松鼠》网站的诊断报告
  9. 回调函数 简单理解
  10. 【打印机】argox入门