广度优先
1. 概念
1、又叫层次遍历,从上往下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问结点,访问完一层就进入下一层,直到没有结点可以访问为止
2、保留全部结点,占用空间大; 无回溯操作(即无入栈、出栈操作),运行速度快。
3、一般需存储产生的所有结点,占用的存储空间要比深度优先搜索大得多,因此,程序设计中,必须考虑溢出和节省内存空间的问题
广度优先算法的核心思想是:从初始节点开始,应用算符生成第一层节点,检查目标节点是否在这些后继节点中,若没有,再用产生式规则将所有第一层的节点逐一扩展,得到第二层节点,并逐一检查第二层节点中是否包含目标节点。若没有,再用算符逐一扩展第二层的所有节点……,如此依次扩展,检查下去,直到发现目标节点为止。即
- 从图中的某一顶点V0开始,先访问V0;
- 访问所有与V0相邻接的顶点V1,V2,…,Vt;
- 依次访问与V1,V2,…,Vt相邻接的所有未曾访问过的顶点;
- 循此以往,直至所有的顶点都被访问过为止。
这种搜索的次序体现沿层次向横向扩展的趋势,所以称之为广度优先搜索。
2. 解题技巧(我的总结)
1> 拓扑排序,课程表问题
每一次都从图中删除没有前驱的顶点,这里并不需要真正的做删除操作,我们可以设置一个入度数组,
每一轮都输出入度为 000 的结点,并移除它、修改它指向的结点的入度(−1-1−1即可),依次得到的
结点序列就是拓扑排序的结点序列。如果图中还有结点没有被移除,则说明“不能完成所有课程的学习”。
1、在开始排序前,扫描对应的存储空间(使用邻接表),将入度为 0 的结点放入队列。
2、只要队列非空,就从队首取出入度为 000 的结点,将这个结点输出到结果集中,并且将这个结点的所有邻接结点(它指向的结点)的入度减 1,在减 1 以后,如果这个被减 1 的结点的入度为 0 ,就继续入队。
3、当队列为空的时候,检查结果集中的顶点个数是否和课程数相等即可。
思考这里为什么要使用队列?(马上就会给出答案。)
在代码具体实现的时候,除了保存入度为 0 的队列,我们还需要两个辅助的数据结构:
1、邻接表:通过结点的索引,我们能够得到这个结点的后继结点;
2、入度数组:通过结点的索引,我们能够得到指向这个结点的结点个数。
这个两个数据结构在遍历题目给出的邻边以后就可以很方便地得到。
题目 | 说明 | 实现 |
---|---|---|
207. 课程表 | 队列存放入度为0的节点 | 我的提交 |
310. 最小高度树 | 从叶子节点向根节点不断扩散到只剩一个(或者多个) | 我的提交 |
2> 双向广度优先搜索,优化时间复杂度
题目 | 说明 | 实现 |
---|---|---|
127. 单词接龙 | leftVisited和rightVisited分别记录左右访问的节点,选取startWord和endWord中邻居少的节点开始扩散 | 我的提交 |
2359. 找到离给定两个节点最近的节点 | 双向同步扩散直到有交集为止 | 我的提交 |
752. 打开转盘锁 | leftVisited和rightVisited分别记录左右访问的节点,选取left和right中邻居少的节点开始扩散 | 我的提交 |
3> 广度优先搜索求最短路径,距离等问题,学会剪枝
题目 | 说明 | 实现 |
---|---|---|
488. 祖玛游戏 | 经分析,只有 相同序列的右端插入相同球(简化),或相同序列的中间插入不同球 两种选择是有效的(保留备用) | 我的提交 |