目录
1. 介绍
2. 图是什么
3.广度优先搜索
【查找最短路径】
【队列】
4.实现图
5. 实现算法
【运行时间】
【拓扑排序】
6. 小结
❑ 学习使用新的数据结构图来建立网络模型。
❑ 学习广度优先搜索,你可对图使用这种算法回答诸如“到X的最短路径是什么”等问题。
❑ 学习有向图和无向图。
❑ 学习拓扑排序,这种排序算法指出了节点之间的依赖关系。
你可以:
❑ 编写国际跳棋AI,计算最少走多少步就可获胜;
❑ 编写拼写检查器,计算最少编辑多少个地方就可将错拼的单词改成正确的单词,如将READED改为READER需要编辑一个地方;
❑ 根据你的人际关系网络找到关系最近的医生。
最短路径问题(shorterst-path problem)。你经常要找出最短路径,这可能是前往朋友家的最短路径,也可能是国际象棋中把对方将死的最少步数。解决最短路径问题的算法被称为广度优先搜索。
要确定如何从双子峰前往金门大桥,需要两个步骤。
(1)使用图来建立问题模型。
(2)使用广度优先搜索解决问题。
图由节点和边组成。一个节点可能与众多节点直接相连,这些节点被称为邻居
Alex 欠RAMA钱
❑ 第一类问题:从节点A出发,有前往节点B的路径吗?
❑ 第二类问题:从节点A出发,前往节点B的哪条路径最短?
第一类:找一个sales
自己的朋友名单 – A他是sales吗?- 是,结束。
- 不是,将A的朋友也加入查找名单
第二类:谁是最近的sales?
朋友是一度;朋友的朋友是二度
一度关系在二度关系之前加入查找名单
队列类似于栈,你不能随机地访问队列中的元素。队列只支持两种操作:入队和出队。
队列是一种先进先出(First In First Out,FIFO)的数据结构,而栈是一种后进先出(Last In First Out, LIFO)的数据结构【可参见阶乘计算】。
列表让你能够将键映射到值。在这里,你要将节点映射到其所有邻居。
graph={}
graph['you']={'Alice','Bob','Hym'}
graph['Bob']={'Tony','Jack'}
graph['Alice']={'',''}
散列表是无序的,因此添加键—值对的顺序无关紧要。
有向图(directed graph),有双肩头才意味着关系双向,否则是单向。
无向图:没肩头。两点连线直接意味着是邻居。
1.创建一个队列,用于存储要拾奋码人
2从队列中弹出一个人
3检查这个人是否是sales
4.是,结束。
不是,将这个人的所有邻居加入列表。
5.回到第二步
6.如果队列为空,说明你的人际关系网中没有sales。
更新队列时,我使用术语“入队”和“出队”,但你也可能遇到术语“压入”和“弹出”。压入大致相当于入队,而弹出大致相当于出队。
from collections import deque
def person_is_seller(person):
# Placeholder condition, modify it according to your criteria
return person.endswith('m')
graph = {}
graph['you'] = {'Alice', 'Bob', 'Hym'}
graph['Bob'] = {'Tony', 'Jackm'}
search_queue = deque()
search_queue += graph['you']
while search_queue: # 只要队列不为空
person = search_queue.popleft() # 就取出其中的第一个人
if person_is_seller(person): # 检查这个人是否是芒果销售商
print(f"{person} is a seller!")
break
else:
search_queue += graph.get(person, set())
else:
print("No seller found.")
结果:Hym is a seller.
这个算法将不断执行,直到满足以下条件之一:❑ 找到一位芒果销售商;❑ 队列变成空的,这意味着你的人际关系网中没有芒果销售商。
*重复人怎么办?.set()创建无序不重复元素集。
检查一个人之前,要确认之前没检查过他,这很重要。为此,你可使用一个列表来记录检查过的人。
from collections import deque
def person_is_seller(person):
# Placeholder condition, modify it according to your criteria
return person.endswith('m')
graph = {}
graph['you'] = {'Alice', 'Bob', 'Hym'}
graph['Bob'] = {'Tony', 'Jackm'}
search_queue = deque()
search_queue += graph['you']
checked_people=set() #set()检查没有重复的人 括号代表调用
while search_queue: # 只要队列不为空
person = search_queue.popleft() # 就取出其中的第一个人
if person in checked_people:
continue
if person_is_seller(person): # 检查这个人是否是芒果销售商
print(f"{person} is a seller!")
break
else:
search_queue += graph.get(person, set())
else:
print("No seller found.")
运行时间至少为O,检查每条边。
你还使用了一个队列,其中包含要检查的每个人。将一个人添加到队列需要的时间是固定的,即为O(1),因此对每个人都这样做需要的总时间为O(人数)。所以,广度优先搜索的运行时间为O(人数+边数),这通常写作O(V+E),其中V为顶点(vertice)数,E为边数。
如果任务A依赖于任务B,在列表中任务A就必须在任务B后面。这被称为拓扑排序,使用它可根据图创建一个有序列表。
举例:族谱。
这种图被称为树。树是一种特殊的图,其中没有往后指的边。
❑ 广度优先搜索指出是否有从A到B的路径。
❑ 如果有,广度优先搜索将找出最短路径。
❑ 面临类似于寻找最短路径的问题时,可尝试使用图来建立模型,再使用广度优先搜索来解决问题。
❑ 有向图中的边为箭头,箭头的方向指定了关系的方向,例如,rama→adit表示rama欠adit钱。
❑ 无向图中的边不带箭头,其中的关系是双向的,例如,ross -rachel表示“ross与rachel约会,而rachel也与ross约会”。
❑ 队列是先进先出(FIFO)的。
❑ 栈是后进先出(LIFO)的。
❑ 你需要按加入顺序检查搜索列表中的人,否则找到的就不是最短路径,因此搜索列表必须是队列。
❑ 对于检查过的人,务必不要再去检查,否则可能导致无限循环。