在Python中对边缘列表进行排序

时间:2016-02-24 17:20:38

标签: python sorting graph

我正在尝试对Python中的唯一边列表进行排序,这样排序就会根据前一个边缘列出边缘列表,这个边缘具有与下一个边缘共享的顶点。我已经有了一个功能来获得“开始”和“结束”边缘。

例如,未排序的边列表就是:

[[0, 5], [2, 4], [4, 5], [1, 2], [0, 6]]

正确排序,这将如下:

[[6, 0], [0, 5], [4, 5], [2, 4], [1, 2]]

,[6,0]是起始边,[1,2]是结束边。

根据我看到的排序方法,排序是基于知道要排序的列表中的哪个索引来完成的,但在这种情况下,索引可以是0或1。

3 个答案:

答案 0 :(得分:1)

from collections import defaultdict

followed_by = defaultdict(list)

def follows(edge1, edge2):  # does edge2 follow edge1
    return edge1 != edge2 and not set(edge1).isdisjoint(set(edge2))

def sorted_path(path, end):

    start = path[-1]

    for follower in followed_by[tuple(start)]:

        if follower in path:
            continue  # avoid circularities

        if follower == end:
            return path + [end]  # solution found

        new_path = sorted_path(path + [follower], end)  # recurse

        if new_path:
            return new_path  # solution found

    return None  # solution not found

# build defaultdict of who follows who

for edge in edges:
    for potential_follower in edges:
        if follows(edge, potential_follower):
            followed_by[tuple(edge)].append(potential_follower)

edges = [[0, 5], [2, 4], [4, 5], [1, 2], [0, 6]]

START = [0, 6]

END = [1, 2]

print(sorted_path([START], END))  # pass the path so far and terminal node

答案 1 :(得分:0)

我将对输入做一些假设:

  1. 起始节点和结束节点仅在列表中出现一次。
  2. 不是起始节点和结束节点的每个节点都会出现两次。
  3. 路径已完全定义(没有不相交的点)。
  4. 鉴于这些假设,我要做的第一件事就是将输入转换为frozenset 1 以便能够利用散列:

    edges = [frozenset(edge) for edge in edges]
    

    现在我构建一个从节点到边缘的映射:

    from collections import defaultdict
    nodes_to_edges = defaultdict(set)
    for edge in edges:
        for node in edge: 
            nodes_to_edges[node].add(edge)
    nodes_to_edges.default_factory = None
    

    现在,路径是通过遍历nodes_to_edges来定义的。在这里,我将手动设置起始节点,因为你知道它。如果您不知道起始节点,则可以在nodes_to_edges中找到一个密钥,其中包含的集合的长度为1。将有2个这样的键(起始节点和结束节点),但如果图形是无向的,那么你可以遍历任何一种方式,它没有任何区别:

    current_node = 6
    path = []
    while nodes_to_edges:
        edge = nodes_to_edges[current_node].pop()
        assert not nodes_to_edges[current_node]
        del nodes_to_edges[current_node]
        path.append(edge)
    
        next_node = next(iter(edge - {current_node}))
        next_edges = nodes_to_edges[next_node]
        if len(next_edges) == 1:  # End node.
            del nodes_to_edges[next_node]
        else:
            next_edges.remove(edge)
        current_node = next_node
    

    当您考虑这种实现时,请记住几个不变量:

    • 每个边缘都存在于nodes_to_edges两次。因此,对于我们添加到path的每个边缘,我们需要从nodes_to_edges中删除两个。
    • 我们可以通过在break子句中添加else语句来摆脱 iff 以上的第三个假设。我认为这也可能有点放松第二个假设 - 新的假设是没有节点有超过2个边连接到它。

    1 我使用了冻结集,因为它看起来像是在使用无向边。如果边缘是定向的,则需要修改它以使用元组。

答案 2 :(得分:0)

以下代码不是一种有效的方法,但完成了您的示例。在这个意义上,独特解决方案的假设被理解为“独特的边缘”。

lst = [[0, 5], [2, 4], [4, 5], [1, 2], [0, 6]]
l=len(lst)
#Your function gives ini and end:
ini = [0, 6]
end = [1, 2]
sol = [ini]
lst.remove(ini)
lst.remove(end)
while len(sol) < l - 1:
    for x in lst:
        if any(y in x for y in sol[-1]):
            sol.append(x)
            lst.remove(sol[-1])
            break
sol.append(end)
print("sol", sol)