广度优先搜索(breadth first search)
图
最短路径问题(shorterst-path problem)
解决最短路径问题的算法被称为广度优先搜索。
最短路径问题解决步骤
(1) 使用图来建立问题模型。
(2) 使用广度优先搜索解决问题。
图的定义
图模拟一组连接。
图由节点(node)和边(edge)组成。一个节点可能与众多节点直接相连,这些节点被称为邻居。图用于模拟不同的东西是如何相连的。
广度优先搜索
广度优先搜索是一种用于图的查找算法,可帮助回答两类问题。
- 第一类问题:从节点A出发,有前往节点B的路径吗?
- 第二类问题:从节点A出发,前往节点B的哪条路径最短?
查找最短路径
在广度优先搜索的执行过程中,搜索范围从起点开始逐渐向外延伸,即先检查一度关系,再检查二度关系。
广度优先搜索不仅查找从A到B的路径,而且找到的是最短的路径。
队列
需要按添加顺序进行检查。可以用队列来实现这种目的
队列类似于栈,不能随机地访问队列中的元素。
队列只支持两种操作:入队和出队。
队列是一种先进先出(First In First Out,FIFO)的数据结构,而栈是一种后进先出(Last InFirst Out,LIFO)的数据结构。
实现图
使用散列表将键映射到值,用来实现图
示例:
>>> graph={}
>>> graph['you']=['alice','bob','claire']
>>> graph
{'you': ['alice', 'bob', 'claire']}
>>> graph['bob']=['anuj','peggy']
>>> graph['alice']=['peggy']
>>> graph['claire']=['thom','jonny']
>>> graph['anuj']=[]
>>> graph['peggy']=[]
>>> graph['thom']=[]
>>> graph['jonny']=[]
>>> graph
{'you': ['alice', 'bob', 'claire'], 'bob': ['anuj', 'peggy'], 'alice': ['peggy'], 'claire': ['thom', 'jonny'], 'anuj': [], 'peggy': [], 'thom': [], 'jonny': []}
键—值对的添加顺序不重要,因为散列表是无序的。
有向图(directed graph)
其中的关系是单向的
无向图(undirected graph)
没有箭头,直接相连的节点互为邻居
实现算法
代码
from collections import deque #使用函数deque来创建一个双端队列
graph={'you': ['alice', 'bob', 'claire'], 'bob': ['anuj', 'peggy'], 'alice': ['peggy'], 'claire': ['thom', 'jonny'], 'anuj': [], 'peggy': [], 'thom': [], 'jonny': []}
def search(name):
search_queue=deque()
search_queue+=graph[name]
searched=[] #这个列表用于记录检查过的元素
while search_queue:
person=search_queue.popleft()
if not person in searched:
if person_is_seller(person):
print(person,'is a mango seller!')
return True
else:
search_queue+=graph[person]
searched.append(person)
return False
def person_is_seller(name):
return name[-1]=='m'
search('you')
原理
- 创建一个队列,用于存储要检查的元素
- 从队列弹出第一个元素
- 检查这个元素,是否满足要求。
- 如果满足返回结果,否则将这个元素的所有邻居加入队列
- 回到第二步。继续检查这些邻居。
- 如果队列为空,就说明没有满足条件的元素
停止条件(满足其一):
- 找到一个满足条件的元素;
- 队列变成空的,这意味队列的所有元素,都没有满足条件
运行时间
广度优先搜索的运行时间为O(V + E),其中V为顶点(vertice)数,E为边数。
解析:
在整个图中搜索元素,就意味着将沿每条边前行(记住,边是从一个人到另一个人的箭头或连接),因此运行时间至少为O(边数)。
还使用了一个队列,其中包含要检查的每个元素。将一元素添加到队列需要的时间是固定的,
即为O(1),因此对每个元素都这样做需要的总时间为O(元素个数)。
扩展
- 树是一种特殊的图,其中没有往后指的边。
- 如果任务A依赖于任务B,在列表中任务A就必须在任务B后面。这被称为拓扑排序,使用它可根据图创建一个有序列表。
小结
- 广度优先搜索指出是否有从A到B的路径。
- 如果有,广度优先搜索将找出最短路径。
- 面临类似于寻找最短路径的问题时,可尝试使用图来建立模型,再使用广度优先搜索来解决问题。
- 有向图中的边为箭头,箭头的方向指定了关系的方向,例如, rama→adit表示rama欠adit钱。
- 无向图中的边不带箭头,其中的关系是双向的,例如, ross - rachel表示“ross与rachel约会,而rachel也与ross约会”。
- 队列是先进先出( FIFO)的。
- 栈是后进先出( LIFO)的。
- 需要按加入顺序检查搜索列表中的元素,否则找到的就不是最短路径,因此搜索列表必须是队列。
- 对于检查过的元素,务必不要再去检查,否则可能导致无限循环。