持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第30天,点击查看活动详情
最短路径:深度优先搜索和广度优先搜索
社交网络由个人和个人之间的关系组成。这些通常建模为图形,其中个体是节点和边缘关系。如果关系对称,则边是无向的;如果关系不对称,则边是定向的。一些社交网络对多种关系进行建模,在这种情况下,边缘上的标签指示关系的类型。
1990年,剧作家约翰·瓜尔(John Guare)写了《六度分离》。该剧背后的可疑前提是“这个星球上的每个人都只被其他六个人隔开”。他的意思是,如果我们建立一个包括每个人的社交网络。
在地球上使用“知道”关系,任何两个个体之间的最短路径最多会通过六个其他节点。一个不太假设的问题是使用Facebook上成对的人之间的“朋友”关系的距离。例如,你可能想知道你是否有一个朋友有一个朋友,而这个朋友是Lady Gaga的朋友。让我们考虑设计一个程序来回答这些问题。
朋友关系(至少在Facebook上)是对称的,例如,如果Sam是Andrea的朋友,Andrea就是Sam的朋友。因此,我们将使用类型图实现社交网络。然后,我们可以将找到您和 Lady Gaga 之间最短联系的问题定义为:
设 G 是表示朋友关系的图形。
对于 G,找到最短的节点序列,[You, ..., Lady Gaga],这样
·伊芬;和 ni+1 是序列中的连续节点,G 中有一个连接 n 的边;和 ni+1'
Eigure 14-9.包含一个递归函数,该函数在二合字母中查找两个节点(开始和结束)之间的最短路径。由于 Graph 是 Digraph 的一个子类,它将适用于我们的 Facebook 问题。
DF 实现的算法是递归深度优先搜索 (DFS) 算法的一个示例。通常,深度优先搜索算法首先选择起始节点的一个子节点。然后,它选择该节点的一个子节点,依此类推,越来越深,直到到达目标节点或没有子节点的节点。然后搜索回溯,返回到具有尚未访问的子节点的最新节点。当探索完所有路径后,它会选择从起点到目标的最短路径(假设有一条)。
代码比我们刚才描述的算法更复杂,因为它必须处理图形的可能性包含循环。它还避免探索比已经找到的最短路径更长的路径。
·该函数shortest_path调用路径 == [](指示当前正在探索的路径为空)和最短 == 无(表示尚未找到从头到尾的路径)的 DF。
DFS 首先选择一个开始的子项。然后,它选择该节点的一个子节点,依此类推,直到它到达节点端或没有未访问的子节点。
o 检查节点是否不在路径中可防止程序陷入循环。
o 检查是否最短 == 无或 len(路径)< len(最短)用于确定继续搜索此路径是否可能产生比目前找到的最佳路径更短的路径。
如果是这样,则递归调用 DF。如果它找到的结束路径不长于迄今为止找到的最佳路径,则更新最短路径。o 当路径上的最后一个节点没有子节点可供访问时,程序将回溯到先前访问的节点并访问该节点的下一个子节点。
·当探索了从开始到结束的所有可能的最短路径时,该函数返回。
图 14-10 包含一些运行图 14= 9 中的代码的代码。图 14-10 中test_sp的函数首先构建如图所示的有向图,然后搜索节点 o 和节点 5 之间的最短路径。
执行时,test_sP 生成输出 当前 DFS 路径:
请注意,在探索路径 o->1->2->3->4 后,它会备份到节点 3 并探索路径 0->1->2->3->5。将其保存为迄今为止最短的成功路径后,它将备份到节点 2 并探索路径 0->1->2->4。当它到达该路径的末尾(节点 4)时,它会一直备份到节点 o,并调查从边缘开始的路径,从 o 到 2。等等。
DFS 算法在图 14-9 中实现。查找具有最少边数的路径。如果边有权重,则不一定会找到最小化边权重总和的路径。但是,很容易修改它。
手指练习:修改 DFS 算法以查找最小化权重总和的路径。假设所有权重都是正整数。
当然,除了深度优先之外,还有其他方法可以遍历图形。另一种常见的方法是广度优先搜索 (BFS)。广度优先遍历首先访问起始节点的所有子节点。如果这些节点都不是结束节点,它将访问每个节点的所有子节点。等等。与通常以递归方式实现的深度优先搜索不同,广度优先搜索通常是以迭代方式实现的。BFS 同时探索许多路径,在每次迭代时为每个路径添加一个节点。由于它按长度升序生成路径,因此以目标作为其最后一个节点找到的第一条路径保证具有最少数量的边。
图 14-11 包含使用广度优先搜索在有向图中查找最短路径的代码。变量 path_queue 用于存储当前正在探索的所有路径。每次迭代首先从path_queue中删除路径并将该路径分配给tmp_path。如果tmp_path中的最后一个节点是 end,则tmp_path是最短路径并返回。否则,将创建一组新路径,每个路径通过添加其子路径之一来扩展tmp_path。然后将这些新路径中的每一个添加到path_queue。
当行
在test_sp末尾添加并执行函数,它打印额外的行
令人欣慰的是,每种算法都找到了相同长度的路径。在这种情况下,他们找到了相同的路径。但是,如果图形在一对节点之间包含多个最短路径,则 DFS 和 BFS 不一定会找到相同的最短路径。
如上所述,BFS 是搜索边最少路径的便捷方法,因为第一次找到路径时,可以保证是这样的路径。
手指练习:考虑一个带有加权边缘的二合字母。BFS找到的第一条路径是否保证最小化边的权重之和?