BFS(广度优先算法)也就这么回事
Posted 写代码的阿宗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BFS(广度优先算法)也就这么回事相关的知识,希望对你有一定的参考价值。
最近在刷关于树的题,其实树也是面试中面试官喜欢考察的问题。对树稍微有了解的同学想必都知道解决这类问题无非是遍历,BFS(广度优先)或者是DFS(深度优先)。这俩其实都不难理解,顾名思义,优先从那边搜索,依次遍历。今天我们来谈谈BFS,DFS留到下次再说。
一个树的结构大概是这样的:
广度优先算法的核心是按层级依次遍历一个树,对于如上这个树来讲,我们遍历的顺序就是12->7->1->9->10->5
。那么怎样才能按照这个顺序遍历整棵树呢,同一个层级的各个节点看起来没什么关联。后期我们就得想办法让它们产生这样的顺序了。
我们先来看一道题目:给定一个二叉树,填充一个数组来代表它的层级关系。把每个层级从左到右填充进一个独立的子数组,返回最后的数组
。
拿到手,发现题目很直接地向我们表达了层级关系,用广度优先没跑了。而且我们也不需要做些额外的操作,单纯地遍历一遍就好了。
我们得先解决让同一层级的节点按顺序处理的问题。我们可以发现,同一层级的节点顺序是由上一层的节点决定的,那我们在处理上一层节点的时候,就已经能知道下一层子节点的顺序了。那其实只需要用某种数据结构把子节点按照顺序存起来就好了,我们就用Queue
这个数据结构吧(其实其它数据结构也都可以,只不过队列适合保存一些按顺序处理用完就丢的东西,而且数据数量一直在变化,会有频繁的放入取出操作,所以相比于List还是Queue更方便)。通过Queue我们可以很方便地对树进行广度优先搜索。我们大概会有如下几个步骤:
向Queue中放入root节点。
只要这个Queue中有元素就一直遍历。
每次遍历时,首先计算一下当前Queue里有多少个元素,这就是这棵树当前层级元素的数量,记作levelSize。
接下来从Queue中移除levelSize这么多个元素,对他们进行符合我们题目的一些操作。
移除每个节点时,把它们的子节点添加进Queue中。
只要Queue中还有元素,说明还有下一个层级,就一直重复步骤3去处理下一个层级。
整个过程就这么多,是不是看起来很简单?但是往往就是这简单的搜索,好多人就是实现不出来。
按照上面分析的步骤,我们来实现以下这个算法:
public static List<List<Integer>> traverse(TreeNode root) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if (root == null)
return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> currentLevel = new ArrayList<>(levelSize);
for (int i = 0; i < levelSize; i++) {
TreeNode currentNode = queue.poll();
currentLevel.add(currentNode.val);
// 把子节点插入到队列中
if (currentNode.left != null)
queue.offer(currentNode.left);
if (currentNode.right != null)
queue.offer(currentNode.right);
}
result.add(currentLevel);
}
return result;
}
只消事先获取到当前层级的数量来控制好循环,再把子节点添加到队列中,整个流程就算是完成了,之后想对每个子元素或者每个层级做什么操作都可以。后面要是让大家逆序啊,S形啊来表示树的结构都很简单了。
通过上面的讨论,想必大家都对BFS有个感性的认识了。接下来,我们再来看一个变形题,来进一步加深对广度优先搜索的印象。
是这样的:给定一个二叉树,把每个层级上的节点跟它在这个层级上的后面一个节点连接起来,每个层级的最后一个节点要指向null
。
这道题跟上面那个题其实一个路子,那我们也按照相同的套路来解题就行咯。还是用广度优先算法,唯一的不同就是,在遍历一个层级的时候,我们要记住前一个结点,以使它跟后面一个节点相连。其余的过程还是跟之前一样一样的。
public static void connect(TreeNode root) {
if (root == null)
return;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode previousNode = null;
int levelSize = queue.size();
//连接当前层及所有的点
for (int i = 0; i < levelSize; i++) {
TreeNode currentNode = queue.poll();
if (previousNode != null)
previousNode.next = currentNode;
previousNode = currentNode;
// 把当前节点的子节点插入队列
if (currentNode.left != null)
queue.offer(currentNode.left);
if (currentNode.right != null)
queue.offer(currentNode.right);
}
}
}
看吧,万变不离其宗,换汤不换药,我们只要掌握了基本题目的解题步骤,再遇到这些变形题时,只要一开始发现了题目的意图,总能按照我们的思路一步一步把算法写出来。而所谓的广度优先算法,在现在看来也就这么回事儿,一般一般。
好了,关于BFS今天就写到这里了,大家平时也可以自己去刷一些类型的题目总结出自己的规律跟套路,形成自己对题目的意识,在网上每天都有新的题目被提出来,掌握核心才能让自己效率最大化。Happy coding~
以上是关于BFS(广度优先算法)也就这么回事的主要内容,如果未能解决你的问题,请参考以下文章