算法图解第六章笔记与习题(广度优先搜索)
文章目录
算法图解pdf百度云链接,提取码:jttg
6.1 图(graph)
**图(graph)模拟一组连接。图由节点(node)和边(edge)**组成。其中边还包含有向边和无向边。
一个节点可能与众多的节点直接相连,直接相连的节点被称为邻居。在一个图中,可以有许多节点和边。
6.2 广度优先搜索
**广度优先搜索(breadth-first-search,BFS)**是一种用于图的查找算法。可帮助回答两类问题。
- 第一类问题:从节点A出发,有前往节点B的路径吗?
- 第二类问题:从节点A出发,前往节点B的哪条路径最短?
对于第一类问题,路径是否存在,广度优先搜索的方式是:
- 创建包含节点A邻居节点的列表。
- 检查这些邻居节点是否为节点B。若为节点B,则搜索完成。
- 若不为节点B,则将该节点其余邻居节点加入到列表中,继续搜索其余节点,直到找到为止。
通过上述的方式,就能遍历整个图中的全部节点,直到搜索到目标节点。这就是广度优先搜索算法。
- 模拟映射关系;
- 防止重复;
- 缓存/记住数据,以免服务器再通过处理来生成它们。
6.3.1 查找最短路径
如果能够按照顺序依次查找被添加到列表中的节点,那么最先找到目标节点的通过的节点就将是最短路径。
为了能将节点按顺序添加,需要使用数据结构队列(queue)。
6.3.2 队列
队列的工作原理与现实生活中的队列完全相同。先排队的人将得到优先的处理。通过这种性质,我们就能将节点按顺序添加到队列中,实现广度优先搜索的查找最短路径的功能。
队列类似于栈,不能随机地访问队列中的元素。
队列只支持两种操作:入队和出队。
队列是一种先进先出(First In First Out,FIFO)的数据结构,而栈是一种后进先出(Last In
**First Out,LIFO)**的数据结构。
6.4 实现图
节点与相邻节点的有向连接用散列表来表示。
表示上图这种这种映射关系的python代码如下:
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"] = []
you
这个节点被映射到了一个数组,因此graph["you"]
是一个数组,包含了you
的全部相邻节点。
同时,在定义图的时候,定义的先后顺序不重要。
上图中的图为有向图(directed graph),其中的关系是单向的。如:BOB节点是ANUJ节点的邻居,反之则不是。**无向图(undirected graph)**没有方向,所有直接相邻的节点都互为邻居。
6.5 实现算法
广度优先搜索在图中查找最短路径的原理如下:
- 创建一个队列,用于存储要检查的节点。
- 从队列中弹出一个节点。
- 检查这个节点是否为目标节点。
- 如果是,则找到目标节点。如果不是,则将这个节点的所有邻居节点加入队列中。
- 若此时队列为空,则目标节点不存在。
- 重复2-5。
from collections import deque # 在python中,可以用deque创建一个双端队列
def is_target_node(name): # 用于检测目标节点thom的简易方式:判断节点最后一个字符是否为m
return name[-1] == 'm'
def search(name):
search_queue = deque() # 创建一个队列
search_queue += graph[name] # 将当前节点的邻居都加入到这个搜索队列中
searched = [] # 该数组用于记录检查过的节点,以避免出现死循环
while search_queue: # 若队列不为空
person = search_queue.popleft() # 弹出队列中第一个节点
if not node in searched: # 判断该节点是否被检查过了,若检查过则跳过。
if is_target_node(node): # 检查该节点是否为目标节点。
print(node + " is target node!") # 若是,则输出节点信息并返回。
return True
else:
search_queue += graph[person] # 若不是目标节点。将该节点的邻居都压入搜索队列
searched.append(person) # 将该节点加入已搜索的数组中
return False # 搜索列表为空,循环结束,则没有目标节点,返回失败。
search("you")
程序结果为返回“thom is target node”
。
广度优先搜索的运行时间为 ( 边 数 顶 点 数 ) O ( 边 数 + 顶 点 数 ) (边数顶点数)O(边数+顶点数) (边数顶点数)O(边数+顶点数),通常写作 ( ) O ( V + E ) ()O(V+E) ()O(V+E)。其中v为顶点(vertice),e为边(edge)。
6.6 小结
- 广度优先搜索指出是否有从A到B的路径。
- 如果有,广度优先搜索将找出最短路径。
- 面临类似于寻找最短路径的问题时,可尝试使用图来建立模型,再使用广度优先搜索来解决问题。
- 有向图中的边为箭头,箭头的方向指定了关系的方向,例如,rama→adit表示rama欠adit钱。
- 无向图中的边不带箭头,其中的关系是双向的,例如,ross - rachel表示“ross与rachel约会,而rachel也与ross约会”。
- 队列是先进先出(FIFO)的。
- 栈是后进先出(LIFO)的。
- 你需要按加入顺序检查搜索列表中的人,否则找到的就不是最短路径,因此搜索列表必须是队列。
- 对于检查过的人,务必不要再去检查,否则可能导致无限循环。
练习
习题6.1-6.2:
最短路径的长度均为2,图略。
习题6.3
- 题干略。
B可行,AC均不可行。
习题6.4
- 根据下图创建一个可行的列表。
一个可行的列表为:起床。刷牙。吃早餐。打包午餐。锻炼。洗澡。穿衣服。
习题6.5
- 树是一种特殊的图,它没有往后指的边。下面哪个图也是树?
AC是树。