Python 根据项目对的交点在项目对之间查找路径

Python 根据项目对的交点在项目对之间查找路径,python,python-3.x,algorithm,list,Python,Python 3.x,Algorithm,List,根据以下条件在成对项之间查找路径最有效的方法是什么(最好是python-3.5+): 对于两个给定对(s1,e1)和(s2,e2),如果e1==s2或e2==s1,则应将它们连接起来。这里没有重复的路径,每对都包含两个不同的名称。此外,每一对只能连接到另一条路径,这里没有孤立的对,即所有对都可以连接 例如,如果输入为: [('Bergerac','Pau'), ('Nice','Montpelier'), ('Pau','Paris'), ('Marseille','Nice'), ('Mont

根据以下条件在成对项之间查找路径最有效的方法是什么(最好是python-3.5+):

对于两个给定对(s1,e1)和(s2,e2),如果
e1==s2
e2==s1
,则应将它们连接起来。这里没有重复的路径,每对都包含两个不同的名称。此外,每一对只能连接到另一条路径,这里没有孤立的对,即所有对都可以连接

例如,如果输入为:

[('Bergerac','Pau'),
('Nice','Montpelier'),
('Pau','Paris'),
('Marseille','Nice'),
('Montpelier','Bergerac'),
('new','Marseille'),
('old','new'),
('Paris', 'last'),
('dsd', 'sds')]
输出应如下所示:

('old', 'new', 'Marseille', 'Nice', 'Montpelier', 'Bergerac', 'Pau', 'Paris', 'last')

注意:这是一个简单的问题,但我找不到任何重复的问题,因此,如果您找到了链接,请将其保留在注释中,以便我可以关闭/删除该问题。

一个解决方案是使用容器并在每次满足条件时对名称进行迭代时对其进行更新

def find_path(names):
    names = names[:]
    total = names.pop()
    while names:
        (c1,*rest, e1)  = total
        for j, (c2,*rest2, e2) in enumerate(names):
            if c1 == e2:
                total = (c2, *rest2, e2, *rest, e1)
                names.pop(j)
            elif e1 == c2:
                total = (c1, *rest, e1, *rest2, e2)
                names.pop(j)
    return total
演示和基准测试:

In [84]: find_path(lst)
Out[84]: 
('old',
 'new',
 'Marseille',
 'Nice',
 'Montpelier',
 'Bergerac',
 'Pau',
 'Paris',
 'last')

In [85]: %timeit find_path(lst)
12.9 µs ± 99.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

一种解决方案是使用容器并在每次满足条件时对名称进行迭代时对其进行更新

def find_path(names):
    names = names[:]
    total = names.pop()
    while names:
        (c1,*rest, e1)  = total
        for j, (c2,*rest2, e2) in enumerate(names):
            if c1 == e2:
                total = (c2, *rest2, e2, *rest, e1)
                names.pop(j)
            elif e1 == c2:
                total = (c1, *rest, e1, *rest2, e2)
                names.pop(j)
    return total
演示和基准测试:

In [84]: find_path(lst)
Out[84]: 
('old',
 'new',
 'Marseille',
 'Nice',
 'Montpelier',
 'Bergerac',
 'Pau',
 'Paris',
 'last')

In [85]: %timeit find_path(lst)
12.9 µs ± 99.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

这是一个简单的线性有向图问题

从所有
s
元素中创建一个集合,从所有
e
元素中创建另一个集合。您的起始位置
此处
此处=s_集-e_集
中的一个元素


此处
作为第一个元素查找边
路径
。现在,只需遍历边:
here=edge[1]
。正如您在回答中所做的那样,您可以从可用列表中弹出该边缘,因为这将加快后续搜索的速度。重复这一段,直到没有更多的边可以遍历。

这是一个简单的线性有向图问题

从所有
s
元素中创建一个集合,从所有
e
元素中创建另一个集合。您的起始位置
此处
此处=s_集-e_集
中的一个元素


此处
作为第一个元素查找边
路径
。现在,只需遍历边:
here=edge[1]
。正如您在回答中所做的那样,您可以从可用列表中弹出该边缘,因为这将加快后续搜索的速度。重复这一段,直到你没有更多的边缘可以遍历。

你可以制作一个字典,然后按照关键->价值链进行操作。诀窍是找出从何处开始,但可以通过查找没有相应值的键来实现。例如:

def find(arr):
    d = {k: v for [k, v] in arr}
    paths = []
    for head in d.keys() - d.values():
        res = []
        while head in d:
            res.append(head)
            head = d[head]
        if len(res) > 2:
            paths.append(res) 
    return paths

%timeit find(arr)
#> 3.95 µs ± 30.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

您可能会有多个链,因此上面的内容会找到所有链,并过滤掉不在任何地方的链(数据中的
dsd
sds
对)。

您只需制作一个字典并遵循键->价值链即可。诀窍是找出从何处开始,但可以通过查找没有相应值的键来实现。例如:

def find(arr):
    d = {k: v for [k, v] in arr}
    paths = []
    for head in d.keys() - d.values():
        res = []
        while head in d:
            res.append(head)
            head = d[head]
        if len(res) > 2:
            paths.append(res) 
    return paths

%timeit find(arr)
#> 3.95 µs ± 30.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)


您可能会有多条链,因此上面的内容会找到所有链,并过滤掉不在任何地方的链(数据中的
dsd
sds
对)。

这是一个常见的面试问题,在这里被问到,尽管完整的解决方案没有明确写出。@pkpnd:这是密切相关的;手头的问题有更多的限制,不需要这样复杂的算法。@pkpnd That link不提供此问题的解决方案。我知道,它甚至不是Python。我没有说这是一个完全相同的副本。这只是一个相关的问题,所以我想你会感兴趣,因为它与你的要求很接近。这是一个常见的面试问题,在这里已经被问过了,尽管完整的解决方案没有明确地写出来。@pkpnd:这是密切相关的;手头的问题有更多的限制,不需要这样复杂的算法。@pkpnd That link不提供此问题的解决方案。我知道,它甚至不是Python。我没有说这是一个完全相同的副本。它只是相关的,所以我认为您会感兴趣,因为它接近您所要求的。在Python-3中,字典属性的设置类似于对象,并且支持大多数
set
的功能。所以只要使用
d.keys()-d.values()
。知道这一点很好,谢谢@Kasramvd。我会做一个编辑。很好的解决方案,但它看起来太凌乱了,我想你可以让它更简洁。另外,考虑到您正在使用多次迭代和所有其他更改,我不确定为什么这比我的答案执行得更快。也许这主要是因为复制和那些POP。在Python-3字典中,属性的设置类似于对象,并且支持大多数
set
的功能。所以只要使用
d.keys()-d.values()
。知道这一点很好,谢谢@Kasramvd。我会做一个编辑。很好的解决方案,但它看起来太凌乱了,我想你可以让它更简洁。另外,考虑到您正在使用多次迭代和所有其他更改,我不确定为什么这比我的答案执行得更快。也许这主要是因为复制和那些pop。这里的
here=s_set-e_set
技巧很好,但在我的回答中,你不必分开开始和结束,甚至不必找到开始。我理解这个问题的基于图形的本质,但这里的重点是,我们的列表实际上是一个已经创建的列表,我很好奇在这个阶段看到其他解决方案来处理这个问题。这里的
here=s_set-e_set
技巧很好,但在我的回答中,你不必分开开始和结束,甚至不必找到答案开始我理解这个问题的基于图形的本质,但这里的要点是,我们的列表实际上是一个已经创建的列表,我很想在这个阶段看到处理这个问题的其他解决方案。在for循环中,
if
elif
可能都不是真的。在这种情况下,
while
循环将永远运行,因为
names
永远不会得到
pop()
ed。这发生在给定数组中,因为第一个测试是使用
('ds