如何在Python中加入链接以获得循环?

如何在Python中加入链接以获得循环?,python,list,functional-programming,iterator,reduce,Python,List,Functional Programming,Iterator,Reduce,我有一个链接列表,想知道连接的路径/循环 我的链接如下所示: [[0, 3], [1, 0], [3, 1]] 我希望答案是这样一个循环(或任何其他匹配循环): 因此,取第一个子列表的第一个元素,然后取第二个元素,然后查找以该元素开始的下一个子列表,然后重新开始 有没有一种优雅的方式来实现这一点?我尝试了reduce函数,但是必须以链接匹配的方式对链接进行排序。我想看看: 提供的功能和算法: 周期检测 把它变成一本字典,循环阅读 def get_cycles(links): ""

我有一个链接列表,想知道连接的路径/循环

我的链接如下所示:

[[0, 3], [1, 0], [3, 1]]
我希望答案是这样一个循环(或任何其他匹配循环):

因此,取第一个子列表的第一个元素,然后取第二个元素,然后查找以该元素开始的下一个子列表,然后重新开始

有没有一种优雅的方式来实现这一点?我尝试了reduce函数,但是必须以链接匹配的方式对链接进行排序。

我想看看:

提供的功能和算法:

  • 周期检测

把它变成一本字典,循环阅读

def get_cycles(links):
    """Get a list of all cycles given a list of links"""
    links_dict = dict(links)
    ret = []
    ret_sets = []
    for starting_point in links_dict:
        cycle = []
        x = starting_point
        while x != None:
            cycle.append(x)
            x = links_dict.get(x)
            if x == starting_point:
                break
        # make sure the cycle is not a repeat (and was a cycle)
        if x != None:
            cycle_set = set(cycle)
            if cycle_set not in ret_sets:
                    ret.append(cycle)
                    ret_sets.append(cycle_set)
    return ret

assert get_cycles([[0, 3], [1, 0], [3, 1]]) == [[0, 3, 1]]
assert get_cycles([[0, 3], [1, 0], [3, 1], [5, 2]]) == [[0, 3, 1]]
assert get_cycles([[0, 3], [1, 0], [3, 1], [5, 2], [2, 5]]) == [[0, 3, 1], [2, 5]]

尝试此操作,假设链接列表中只存在一个循环:

def cycle_list(links):
    d = dict(links)
    ele = links[0][0]
    nxt = d[ele]
    lst = [ele]
    seen = set(lst)
    while nxt not in seen:
        lst.append(nxt)
        seen.add(nxt)
        ele = nxt
        nxt = d[ele]
    return lst
以你的例子:

cycle_list([[0, 3], [1, 0], [3, 1]])
> [0, 3, 1]

如果链接列表中可能存在多个循环(你在问题中没有提到),那么最好使用David Robinson的答案。

如果你知道有一个循环,并且所有链接都在循环中(或者至少没有“拆分”)在方向中,表示从任何给定点只有一条路,您可以使用以下方法:

def get_cycle(data):
    d = dict(data)
    first = data[0][0]
    current = d[first]
    path = [first]
    while True:
        if current == first:
            return path
        else:
            path.append(current)
            current = d[current]
它所做的是遍历给定的数据,从第一个链接的第一个点开始。然后它只跟随所有链接,直到到达路径的开头。当它到达路径的开头时,它返回路径

简单,而且我相信它相当有效。

考虑使用该软件包:

输出:

>> [0, 3, 1]

使用发电机有一种非常优雅的方法:

def cycle(lst, val, stop=None):
    d = dict(lst)
    stop = stop if stop is not None else val
    while True:
        yield val
        val = d.get(val, stop)
        if val == stop: break
首先,它允许自然迭代:

>>> for x in cycle([[0, 3], [1, 0], [3, 1]], 0):
....    print x
....
0
3
1
其次,它允许轻松创建列表:

>>> list(cycle([[0, 3], [1, 0], [3, 1]], 0))
[0, 3, 1]
最终,它允许无限项生成:

>>> generator = cycle([[0, 3], [1, 0], [3, 1]], 0, Ellipsis)
>>> generator.next()
... 0
>>> generator.next()
... 3
>>> generator.next()
... 1
>>> generator.next()
... 0
>>> generator.next()
... 3
>>> generator.next()
... 1
>>> generator.next()
... 0
>>> generator.next()
... 3

使用itertools.permutations,这将获得一组唯一循环:

import itertools

g = [(0,3), (1,0), (3,1), (1,4), (4,3)]

cycles = {}
for edges in itertools.permutations(g):
    start = prev = edges[0]
    for i, edge in enumerate(edges[1:], start=1):
        if prev[1] != edge[0]:
            break
        if edge[1] != start[0]:
            prev = edge
            continue
        cycles.update({tuple(sorted(edges[0:i+1])): edges[0:i+1]})
        break

result = []
for cycle in cycles.values():
    result.append([edge[0] for edge in cycle])

print result

本例中的结果是
[[3,1,0],[4,3,1]]

导入中有一个输入错误。虽然我编写了一个很好的手动解决方案,但我+1这一次是因为networkx是一个美丽的野兽。这是我唯一一次在numpy之外看到有人使用
省略号!我通常使用
object()
。并不是说椭圆有什么问题:pElipsis作为一个哨兵值真的很好,因为几乎没有什么东西可以意外地变成它,而且它是一个不需要实例化的单子。此外,这对无限代人来说是有意义的。
>>> generator = cycle([[0, 3], [1, 0], [3, 1]], 0, Ellipsis)
>>> generator.next()
... 0
>>> generator.next()
... 3
>>> generator.next()
... 1
>>> generator.next()
... 0
>>> generator.next()
... 3
>>> generator.next()
... 1
>>> generator.next()
... 0
>>> generator.next()
... 3
import itertools

g = [(0,3), (1,0), (3,1), (1,4), (4,3)]

cycles = {}
for edges in itertools.permutations(g):
    start = prev = edges[0]
    for i, edge in enumerate(edges[1:], start=1):
        if prev[1] != edge[0]:
            break
        if edge[1] != start[0]:
            prev = edge
            continue
        cycles.update({tuple(sorted(edges[0:i+1])): edges[0:i+1]})
        break

result = []
for cycle in cycles.values():
    result.append([edge[0] for edge in cycle])

print result