目录

图简介

图是什么

广度优先搜索

查找最短路径

队列

实现图

实现算法

运行时间

小结

示例代码

C++

Python

C#

Java

JS


广度优先搜索让你能够找出两样东西之间的最短距离,不过最短距离的含义有很多!使用广度优先搜索可以:
 编写国际跳棋AI,计算最少走多少步就可获胜;
 编写拼写检查器,计算最少编辑多少个地方就可将错拼的单词改成正确的单词,如将
READED改为READER需要编辑一个地方;
 根据你的人际关系网络找到关系最近的医生。

图简介

还有其他前往金门大桥的路线,但它们更远(需要四步)。这个算法发现,前往金门大桥的
最短路径需要三步。这种问题被称为最短路径问题(shorterst-path problem)。你经常要找出最短
路径,这可能是前往朋友家的最短路径,也可能是国际象棋中把对方将死的最少步数。解决最短
路径问题的算法被称为广度优先搜索。

要确定如何从双子峰前往金门大桥,需要两个步骤。
(1) 使用图来建立问题模型。
(2) 使用广度优先搜索解决问题。

图是什么

就这么简单!图由节点和边组成。一个节点可能与众多节点直接相连,这些节点被称为邻居。
在前面的欠钱图中, Rama是Alex的邻居。 Adit不是Alex的邻居,因为他们不直接相连。但Adit既
是Rama的邻居,又是Tom的邻居。

广度优先搜索

广度优先搜索是一种用于图的查找算法,可帮助回答两类问题。
 第一类问题:从节点A出发,有前往节点B的路径吗?
 第二类问题:从节点A出发,前往节点B的哪条路径最短?

这样一来,你不仅在朋友中查找,还在朋友的朋友中查找。别忘了,你的目标是在你的人际
关系网中找到一位芒果销售商。因此,如果Alice不是芒果销售商,就将其朋友也加入到名单中。
这意味着你将在她的朋友、朋友的朋友等中查找。使用这种算法将搜遍你的整个人际关系网,直
到找到芒果销售商。这就是广度优先搜索算法。

查找最短路径

在你看来,一度关系胜过二度关系,二度关系胜过三度关系,以此类推。因此,你应先在一
度关系中搜索,确定其中没有芒果销售商后,才在二度关系中搜索。广度优先搜索就是这样做的!
在广度优先搜索的执行过程中,搜索范围从起点开始逐渐向外延伸,即先检查一度关系,再检查
二度关系。顺便问一句:将先检查Claire还是Anuj呢? Claire是一度关系,而Anuj是二度关系,因
此将先检查Claire,后检查Anuj。

你按顺序依次检查名单中的每个人,看看他是否是芒果销售商。这将先在一度关系中查找,
再在二度关系中查找,因此找到的是关系最近的芒果销售商。广度优先搜索不仅查找从A到B的
路径,而且找到的是最短的路径。

注意,只有按添加顺序查找时,才能实现这样的目的。换句话说,如果Claire先于Anuj加入
名单,就需要先检查Claire,再检查Anuj。如果Claire和Anuj都是芒果销售商,而你先检查Anuj
再检查Claire,结果将如何呢?找到的芒果销售商并非是与你关系最近的,因为Anuj是你朋友的
朋友,而Claire是你的朋友。因此,你需要按添加顺序进行检查。有一个可实现这种目的的数据
结构,那就是队列(queue) 。

队列

队列的工作原理与现实生活中的队列完全相同。
假设你与朋友一起在公交车站排队,如果你排在他前
面,你将先上车。队列的工作原理与此相同。队列类
似于栈,你不能随机地访问队列中的元素。队列只支
持两种操作: 入队和出队。

实现图

首先,需要使用代码来实现图。图由多个节点组成。
每个节点都与邻近节点相连,如果表示类似于“你→Bob”
这样的关系呢?好在你知道的一种结构让你能够表示这种关
系,它就是散列表!
记住,散列表让你能够将键映射到值。在这里,你要将节
点映射到其所有邻居。

Anuj、 Peggy、 Thom和Jonny都没有邻居,这是因为虽然有指向他们的箭头,但没有从他们
出发指向其他人的箭头。这被称为有向图(directed graph) ,其中的关系是单向的。因此, Anuj
是Bob的邻居,但Bob不是Anuj的邻居。 无向图(undirected graph)没有箭头,直接相连的节点互
为邻居。例如,下面两个图是等价的。

graph = {}
graph["you"] = ["alice", "bob", "claire"]print(graph["you"])for items in graph["you"]:print(items)

实现算法

先概述一下这种算法的工作原理。

这个算法将不断执行,直到满足以下条件之一:
 找到一位芒果销售商;
 队列变成空的,这意味着你的人际关系网中没有芒果销售商。
Peggy既是Alice的朋友又是Bob的朋友,因此她将被加入队列两次:一次是在添加Alice的朋
友时,另一次是在添加Bob的朋友时。因此,搜索队列将包含两个Peggy。但你只需检查Peggy一次,看她是不是芒果销售商。如果你检查两次,就做了无用功。因此,检查完一个人后,应将其标记为已检查,且不再检查他。
如果不这样做,就可能会导致无限循环。假设你的人际关系网类似于下面这样。

运行时间

如果你在你的整个人际关系网中搜索芒果销售商,就意味着你将沿每条边前行(记住,边是
从一个人到另一个人的箭头或连接),因此运行时间至少为O(边数)。
你还使用了一个队列,其中包含要检查的每个人。将一个人添加到队列需要的时间是固定的,
即为O(1),因此对每个人都这样做需要的总时间为O(人数)。所以,广度优先搜索的运行时间为
O(人数 + 边数),这通常写作O(V + E),其中V为顶点(vertice)数, E为边数。

小结

 广度优先搜索指出是否有从A到B的路径。
 如果有,广度优先搜索将找出最短路径。
 面临类似于寻找最短路径的问题时,可尝试使用图来建立模型,再使用广度优先搜索来
解决问题。
 有向图中的边为箭头,箭头的方向指定了关系的方向,例如, rama→adit表示rama欠adit钱。
 无向图中的边不带箭头,其中的关系是双向的,例如, ross - rachel表示“ross与rachel约
会,而rachel也与ross约会”。
 队列是先进先出(FIFO)的。
 栈是后进先出(LIFO)的。
 你需要按加入顺序检查搜索列表中的人,否则找到的就不是最短路径,因此搜索列表必
须是队列。
 对于检查过的人,务必不要再去检查,否则可能导致无限循环

示例代码

C++

#include <iostream>
#include <map>
#include <list>
#include <queue>
using namespace std;
template <typename T>
class Graph {map <T, list<T>> adjList;public:Graph(){}void addEdge(T u, T v, bool bidir = true){adjList[u].push_back(v);if (bidir)adjList[v].push_back(u);}void printAdjList(){for (auto key : adjList){cout << key.first << "->";for (auto neighbours : key.second)cout << neighbours << ",";cout << endl;}}void bfs(T src){queue<T> q;map<T, bool> visited;q.push(src);visited[src] = true;while (!q.empty()){T node = q.front();cout << node << " ,";q.pop();//push the neighboursfor (auto neighbours : adjList[node]){if (!visited[neighbours]){q.push(neighbours);visited[neighbours] = true;}}}}
};int main() {Graph<int> g;//adding the edges in the Graphg.addEdge(0, 1);g.addEdge(1, 2);g.addEdge(0, 4);g.addEdge(2, 4);g.addEdge(2, 3);g.addEdge(3, 5);g.addEdge(3, 4);cout << "The Graph is" << endl;g.printAdjList();cout << endl;cout << "The Breadth First Search from Node 0" << endl;g.bfs(0);system("pause");
}

C++11

#include <iostream>
#include <unordered_map>
#include <string>
#include <vector>
#include <queue>
#include <unordered_set>using std::cout;
using std::endl;bool is_seller(const std::string& name) {return name.back() == 'm';
}template <typename T>
bool search(const T& name, const std::unordered_map<T, std::vector<T>>& graph) {std::queue<T> search_queue;std::unordered_set<T> searched;// add all friends to search queuefor (auto friend_name : graph.find(name)->second) {search_queue.push(friend_name);}while (!search_queue.empty()) {T person = search_queue.front();//    T& person = search_queue.front();search_queue.pop();// only search this person if you haven't already searched them.if (searched.find(person) == searched.end()) {if (is_seller(person)) {cout << person << " is a mango seller!" << endl;return true;}std::vector<T> friend_list = graph.find(person)->second;// add all friends of a person to search queuefor (T friend_name : friend_list) {search_queue.push(friend_name);}// mark this person as searchedsearched.insert(person);}}return false;
}int main() {std::unordered_map<std::string, std::vector<std::string>> graph;graph.insert({ "you",{ "alice", "bob", "claire" } });graph.insert({ "bob",{ "anuj", "peggy" } });graph.insert({ "alice",{ "peggy" } });graph.insert({ "claire",{ "thom", "jonny" } });graph.insert({ "anuj",{} });graph.insert({ "peggy",{} });graph.insert({ "thom",{} });graph.insert({ "jonny",{} });std::string name = "you";bool result = search(name, graph);cout << "Found mango seller: " << result << endl;
}

Python

from collections import dequedef person_is_seller(name):return name[-1] == 'm'graph = {}
graph["you"] = ["alice", "bob", "claire"]
graph["bob"] = ["anuj", "peggy"]
graph["alice"] = ["peggy"]
graph["claire"] = ["thom", "jonny"]
graph["anuj"] = []
graph["peggy"] = []
graph["thom"] = []
graph["jonny"] = []def search(name):search_queue = deque()search_queue += graph[name]# This array is how you keep track of which people you've searched before.searched = []while search_queue:person = search_queue.popleft()# Only search this person if you haven't already searched them.if person not in searched:if person_is_seller(person):print person + " is a mango seller!"return Trueelse:search_queue += graph[person]# Marks this person as searchedsearched.append(person)return Falsesearch("you")

C#

using System;
using System.Collections.Generic;
using System.Linq;namespace ConsoleApplication
{public class Program{private static Dictionary<string, string[]> _graph = new Dictionary<string, string[]>();public static void Main(string[] args){_graph.Add("you", new[] { "alice", "bob", "claire" });_graph.Add("bob", new[] { "anuj", "peggy" });_graph.Add("alice", new[] { "peggy" });_graph.Add("claire", new[] { "thom", "jonny" });_graph.Add("anuj", Array.Empty<string>());_graph.Add("peggy", Array.Empty<string>());_graph.Add("thom", Array.Empty<string>());_graph.Add("jonny", Array.Empty<string>());Search("you");}private static bool Search(string name){var searchQueue = new Queue<string>(_graph[name]);var searched = new List<string>();while (searchQueue.Any()){var person = searchQueue.Dequeue();if (!searched.Contains(person)){if (PersonIsSeller(person)){Console.WriteLine($"{person} is a mango seller");return true;}else{searchQueue = new Queue<string>(searchQueue.Concat(_graph[person]));searched.Add(person);}}}return false;}private static bool PersonIsSeller(string name){return name.EndsWith("m");}}
}

Java

import java.util.*;public class BreadthFirstSearch {private static Map<String, List<String>> graph = new HashMap<>();private static boolean search(String name) {Queue<String> searchQueue = new ArrayDeque<>(graph.get(name));// This list is how you keep track of which people you've searched before.List<String> searched = new ArrayList<>();while (!searchQueue.isEmpty()) {String person = searchQueue.poll();// Only search this person if you haven't already searched themif (!searched.contains(person)) {if (person_is_seller(person)) {System.out.println(person + " is a mango seller!");} else {searchQueue.addAll(graph.get(person));// Marks this person as searchedsearched.add(person);}}}return false;}private static boolean person_is_seller(String name) {return name.endsWith("m");}public static void main(String[] args) {graph.put("you", Arrays.asList("alice", "bob", "claire"));graph.put("bob", Arrays.asList("anuj", "peggy"));graph.put("alice", Arrays.asList("peggy"));graph.put("claire", Arrays.asList("thom", "jonny"));graph.put("anuj", Collections.emptyList());graph.put("peggy", Collections.emptyList());graph.put("thom", Collections.emptyList());graph.put("jonny", Collections.emptyList());search("you");}
}

JS

'use strict';function person_is_seller(name) {return name[name.length-1] === 'm';
}const graph = {};
graph["you"] = ["alice", "bob", "claire"];
graph["bob"] = ["anuj", "peggy"];
graph["alice"] = ["peggy"];
graph["claire"] = ["thom", "jonny"];
graph["anuj"] = [];
graph["peggy"] = [];
graph["thom"] = [];
graph["jonny"] = [];function search(name) {let search_queue = [];search_queue = search_queue.concat(graph[name]);// This array is how you keep track of which people you've searched before.const searched = [];while (search_queue.length) {let person = search_queue.shift();// Only search this person if you haven't already searched themif (searched.indexOf(person) === -1) {if (person_is_seller(person)) {console.log(person + ' is a mango seller!');return true;} else {search_queue = search_queue.concat(graph[person]);// Marks this person as searchedsearched.push(person);}}}return false;
}search('you'); // thom is a mango seller!

《图解算法》学习笔记之广度优先搜索(breadth-first search, BFS)相关推荐

  1. 图解算法学习笔记(六):广度优先搜索

    目录 1)图简介 2)图是什么 3)广度优先搜索 4)实现图 5)实现算法 6)小结 本章内容; 学习使用新的数据结构图来建立网络模型: 学习广度优先搜索: 学习有向图和无向图: 学习拓扑排序,这种排 ...

  2. LQ训练营(C++)学习笔记_广度优先搜索

    这里写目录标题 四.广度优先搜索 1.队列的概念 2.小朋友报数问题 2.1 问题描述 2.2 代码实现 3.广度优先搜索概念 4.走迷宫问题 4.1 问题描述 4.2 代码实现 5.过河卒问题 5. ...

  3. 图解算法学习笔记(七):狄克斯特拉算法

    目录 1)使用狄克斯特拉算法 2)术语 3)实现 4)小结 本章内容; 介绍加权图,提高或降低某些边的权重: 介绍狄克斯特拉算法,找出加权图中前往X的最短路径: 介绍图中的环,它导致狄克斯特拉算法不管 ...

  4. 图解算法学习笔记(目录)

    今天遇到一本好书,如下,书很薄,不到200页,有将近400张图片,算法介绍的很有趣.这也是我读的第三本袁国忠先生翻译的书,向两位致敬. 目录大致如下; 第1章:二分查找和大O表示法: 第2章:数组和链 ...

  5. 图解算法学习笔记(一): 算法简介

    本章内容: 编写第一种查找算法--二分查找. 学习如何谈论算法的运行时间--大O表示法. 1) 算法是一组完成任务的指令,任何代码片段都可视为算法. 2)二分查找:一种查找算法,其输入是一个有序的元素 ...

  6. 图解算法学习笔记(九):动态规划

    目录 (1)背包问题 (2)最长公共子串 (3)小结 本章内容: 学习动态规划,它将问题分成小问题,并先着手解决这些小问题. 学习如何设计问题的动态规划解决方案. (1)背包问题 我们再看第八章的背包 ...

  7. 图解算法学习笔记(八):贪婪算法

    目录 (1)背包问题 (2)集合覆盖问题 (3)NP完全问题 (4)小结 本章内容: 学习如何处理没有快速算法的问题(NP完全问题). 学习近似算法,使用它们找到NP问题的近似解. 学习贪婪策略. ( ...

  8. 图解算法学习笔记(四):快速排序

    目录 1) 示例1: 2)快速排序 3) 再谈大O表示法 4)小结 本章内容:学习分而治之,快速排序 1) 示例1: 假设你是农场主,有一小块土地,你要将这块地均匀分成方块,且分出的方块尽可能大.如何 ...

  9. 图解算法学习笔记(五):散列表

    目录 1)示例1: 2)散列函数 3)应用案例 4)冲突 5)性能 6)小结 本章内容: 学习散列表,最有用的数据结构之一. 学习散列表的内部机制:实现.冲突和散列函数. 1)示例1: 假设你在一家杂 ...

  10. 图解算法学习笔记(三):递归

    本章内容: 学习递归:如何将问题分解成基线条件和递归条件.     1) 每个递归函数都有两部分:基线条件(base case)和递归条件(recursive base).例如:打印3...2...1 ...

最新文章

  1. 【1】 pythonic modern c++:字符串
  2. python图片识别-python图像识别
  3. 菲波那契数列(信息学奥赛一本通-T1201)
  4. Swift - UIBezierPath
  5. 低代码开发平台是什么
  6. void main(){char str[] = “\tab\n\014\\\“;printf(“%d“,strlen(str))}
  7. 神器 Nginx 的学习手册(建议收藏吃灰)
  8. Julia : Array !, [1,2] !=[1 2]'
  9. 解决TypeError: conv2d() received an invalid combination of arguments
  10. html旋转360度特效,CSS3图片旋转特效(360/60/-360度)
  11. 带农历的html日历插件,魔镜日历插件-可生成带农历的日历月历年历-CDR插件
  12. Csdn富文本编辑器中使用Emoji表情包
  13. 编辑SRT字幕,添加在视频中播放
  14. 眼底图像血管增强与分割--(5)基于Hessian矩阵的Frangi滤波算法
  15. win8宽带连接服务器未响应,关于Win8系统连接宽带错误的解决方法介绍
  16. sanp 7 1200plc通信
  17. Flutter项目错误解决:/.pub-cache/hosted/pub.dartlang.org/flutter_advanced_networkimage-0.5.0
  18. nodejs ftp文件服务器,NodeJS 连接FTP服务器
  19. 充电枪cp信号控制板_一种带CC/CP及电子锁的车载充电机电路的制作方法
  20. Red Hat Linux 9.0软件功能

热门文章

  1. 音频脉冲c语言程序,基于单片机的音乐发声器的设计(完整版,含程序和电路图).doc...
  2. C++ wchar_t转char
  3. c# Settings.settings 存储数据,读取数据
  4. windows GDI开发
  5. fedora html 编辑器,分享|在 Fedora 上使用 Pitivi 编辑视频
  6. java中多态含有math类_Java面试题汇总《Java基础、语法51-55》
  7. linux数据结构视频,数据结构视频教程
  8. yolov4训练自己的数据 灰度图像_还在为图像训练数据少发愁吗?那是因为你还不会这几招...
  9. 关于Spring Cloud Eureka
  10. C++算法之 一句话推断一个整数是不是2 的整数次方