在图中找到最长的路径

Posted

技术标签:

【中文标题】在图中找到最长的路径【英文标题】:finding longest path in a graph 【发布时间】:2015-06-01 22:54:03 【问题描述】:

我正在尝试解决一个程序,其中我必须找到给定路线列表连接的最大城市数。

例如: 如果给定的路线是[['1', '2'], ['2', '4'], ['1', '11'], ['4', '11']] 那么连接的最大城市将是4 限制是我不能访问我已经访问过的城市。

我需要想法,比如如何进步。

现在,我的想法是,如果我能够创建一个字典,其中城市作为键,它连接到多少其他城市作为它的值,我会接近解决方案(我希望)。 例如:我的字典将是'1': ['2', '11'], '4': ['11'], '2': ['4'] 对于上述给定的输入。 如果我遗漏任何东西,我希望得到帮助以进一步进行指导。

【问题讨论】:

算法方面可以看depth-first search或者breadth-first search @jedwards 谢谢。我浏览了这些页面。如果您能帮助我实现它或详细说明如何实现它,我将不胜感激。 最长路径问题是一个NP难题。见en.m.wikipedia.org/wiki/Longest_path_problem。您可能想使用图形库或使用已知算法来解决它。 【参考方案1】:

您可以使用defaultdict 从您的边/路径列表中创建“图表”:

edges = [['1', '2'], ['2', '4'], ['1', '11'], ['4', '11']]

G = defaultdict(list)
for (s,t) in edges:
    G[s].append(t)
    G[t].append(s)

print G.items()

输出:

[ ('1', ['2', '11']), ('11', ['1', '4']), ('2', ['1', '4']), ('4', ['2', '11']) ]

请注意,我在两个方向上都添加了边,因为您使用的是无向图。因此,对于边缘 (a,b),G[a] 将包含 bG[b] 将包含 a

由此,您可以使用depth-first search 或breadth-first search 之类的算法来发现图中的所有路径。

在下面的代码中,我使用了 DFS:

def DFS(G,v,seen=None,path=None):
    if seen is None: seen = []
    if path is None: path = [v]

    seen.append(v)

    paths = []
    for t in G[v]:
        if t not in seen:
            t_path = path + [t]
            paths.append(tuple(t_path))
            paths.extend(DFS(G, t, seen[:], t_path))
    return paths

你可以使用的:

G = defaultdict(list)
for (s,t) in edges:
    G[s].append(t)
    G[t].append(s)

print DFS(G, '1')

输出:

[('1', '2'), ('1', '2', '4'), ('1', '2', '4', '11'), ('1', '11' '), ('1', '11', '4'), ('1', '11', '4', '2')]

完整的代码,最后一点显示最长的路径:

from collections import defaultdict

def DFS(G,v,seen=None,path=None):
    if seen is None: seen = []
    if path is None: path = [v]

    seen.append(v)

    paths = []
    for t in G[v]:
        if t not in seen:
            t_path = path + [t]
            paths.append(tuple(t_path))
            paths.extend(DFS(G, t, seen[:], t_path))
    return paths


# Define graph by edges
edges = [['1', '2'], ['2', '4'], ['1', '11'], ['4', '11']]

# Build graph dictionary
G = defaultdict(list)
for (s,t) in edges:
    G[s].append(t)
    G[t].append(s)

# Run DFS, compute metrics
all_paths = DFS(G, '1')
max_len   = max(len(p) for p in all_paths)
max_paths = [p for p in all_paths if len(p) == max_len]

# Output
print("All Paths:")
print(all_paths)
print("Longest Paths:")
for p in max_paths: print("  ", p)
print("Longest Path Length:")
print(max_len)

输出:

所有路径: [('1', '2'), ('1', '2', '4'), ('1', '2', '4', '11'), ('1', '11' '), ('1', '11', '4'), ('1', '11', '4', '2')] 最长路径: ('1', '2', '4', '11') ('1', '11', '4', '2') 最长路径长度: 4

注意,搜索的“起点”由DFS 函数的第二个参数指定,在本例中为'1'


更新:正如 cmets 中所讨论的,上面的代码假设您已经有了一个起点(特别是代码使用标记为 '1' 的节点)。

如果您没有这样的起点,更通用的方法是从 每个 节点开始执行搜索,并取整体最长的。 (注意:实际上,你可能比这更聪明)

换行

all_paths = DFS(G, '1')

all_paths = [p for ps in [DFS(G, n) for n in set(G)] for p in ps]

会给你任意两点之间的最长路径。

(这是一个愚蠢的列表推导,但它只允许我更新一行。更清楚地说,它相当于以下内容:

all_paths = []
for node in set(G.keys()):
    for path in DFS(G, node):
        all_paths.append(path)

from itertools import chain
all_paths = list(chain.from_iterable(DFS(G, n) for n in set(G)))

)。

【讨论】:

很好的解释,谢谢 如果这是一个有向无环图呢?我会修改 edge: 循环中的 for (s,t) 吗? @ForeverLearning,是的,您可能希望从该循环中删除 G[t].append(s) 这种方法对于 DAG 的特殊情况并不是最有效的(坦率地说,我什至不确定它是否是“最有效的”对于任何情况)是你可以做的“技巧”(即拓扑排序)来实现寻找最长路径的线性时间算法。答案中代码的复杂性远非线性。对于像 OP 所述的一般图表,这种优化被认为是不可能的。但对于 DAG,它们是,这不是我在这里使用的方法(因为它不适用于一般图表)。 我来这里是为了寻找类似问题的解决方案,但我不确定这段代码是否符合我的想法。如果我在边缘添加一个新顶点 ['5','1'],我不会得到更长的路径,这似乎是我应该的。我是否误解了问题和/或答案?【参考方案2】:

这是我的代码,适用于示例中的输入,但如果我稍微调整输入,代码无法给出正确的连接城市数量。

def dfs(graph, start, visited=None):
if visited is None:
    visited = set()
visited.add(start)
#had to do this for the key error that i was getting if the start doesn't
#have any val.
if isinstance(start,str) and start not in graph.keys():
    pass
else:
    for next in set(graph[start]) - visited:
        dfs(graph, next, visited)
return visited

def maxno_city(input1):
totalcities = []
max_nocity = 0
routedic = 
#dup = []
rou = []
for cities in input1:
    cities = cities.split('#')
    totalcities.append(cities)
print (totalcities)
for i in totalcities:
    if i[0] in routedic.keys():
        routedic[i[0]].append(i[1])
    else:
        routedic.update(i[0]:[i[1]])
print(routedic)
keys = routedic.keys()
newkeys = []
for i in keys:
    newkeys.append(i)
print (newkeys)
newkeys.sort()
print (newkeys)
expath = dfs(routedic,newkeys[0])
return(len(expath))

上面给定输入的输出是4,我得到4,但是如果输入更改为这样的: ['1#2','2#3','1#11','3#11','4#11','4#5','5#6','5#7','6#7','4#12','8#12','9#12','8#10','9#10',8#9] 我的代码失败了。

谢谢, 学习忍者:D

【讨论】:

以上是关于在图中找到最长的路径的主要内容,如果未能解决你的问题,请参考以下文章

在图中找到最长的路径,其中每个节点最多有两条传入边和两条传出边

特定类型图中的最长路径

构造一个将两个 DAG 作为输入并返回在两者中找到的最长路径的算法

在有向无环图中求最长路径

使用Gremlin在图中查找最长的循环路径

如何在非二叉树中找到最长路径并将其返回到向量中?