Python 实现拓扑排序

hresh 689 0

Python 实现拓扑排序

什么是拓扑排序

在计算机科学领域,有向图的拓扑排序是其顶点的线性排序,使得对于从顶点 u 到顶点 v 的每个有向边 uv,u 在排序中都在 v 之前。

例如,图形的顶点可以表示要执行的任务,并且边可以表示一个任务必须在另一个任务之前执行的约束;在这个应用中,拓扑排序只是一个有效的任务顺序。

如果且仅当图形没有定向循环,即如果它是有向无环图(DAG),则拓扑排序是可能的。

任何 DAG 具有至少一个拓扑排序,并且已知这些算法用于在线性时间内构建任何 DAG 的拓扑排序。

在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序(英语:Topological sorting):

  • 每个顶点出现且只出现一次;
  • 若 A 在序列中排在 B 的前面,则在图中不存在从 B 到 A 的路径。

如图所示:

拓扑排序

直观法解 DAG

如上的无环有向图,v 表示顶点:v=['a','b','c','d','e'],e 表示有向边:e=[('a','b'),('a','d'),('b','c'),('d','c'),('d','e'),('e','c')]。

def indegree(v,e):
    if len(v) == 0:
        return None
    v_tmp = v[:]
    e_tmp = e[:]

    for x in e:
        if x[1] in v_tmp:#v_tmp保留没有被指向的顶点
            v_tmp.remove(x[1])

    if len(v_tmp) == 0:#如果v_tmp为空,则说明有回环
        return -1

    for i in v_tmp:
        for index,x in enumerate(e_tmp):
            if i in x:
                e.remove(x)#将包含v_tmp中的顶点的有向边删除掉,清理完的e下次再做判断

    for i in v_tmp:
        v.remove(i)#清理已经处理过的顶点

    return v_tmp

def topSort(v,e):
    result = []
    while True:
        nodes = indegree(v,e)
        if nodes == None:
            break
        if nodes == -1:
            print('there\'s a circle.')
            return None
        result.extend(nodes)

    return result

if __name__ == '__main__':
    v = ['a', 'b', 'c', 'd', 'e']
    e = [('a', 'b'), ('a', 'd'), ('b', 'c'), ('d', 'c'), ('d', 'e'), ('e', 'c')]
    print(topSort(v,e))

归简法解 DAG

归简法求解拓扑排序,计算拓扑结构中每个节点的入度,移除入度为 0 的节点,(入度为 0 表示没有任何节点指向它),然后再判断解决剩下的节点。

def topological_sort(graph):
    in_degrees = dict((u, 0) for u in graph)
    for u in graph:#遍历键值
        for v in graph[u]:  # 根据键找出值也就是下级节点
            in_degrees[v] += 1  # 对获取到的下级节点的入度加 1

    # 循环结束之后的结果: {'a': 0, 'b': 1, 'c': 1, 'd': 2, 'e': 1, 'f': 4}
    Q = [u for u in graph if in_degrees[u] == 0]  # 找出入度为 0 的节点
    in_degrees_zero = []
    while Q:
        u = Q.pop()  # 默认从最后一个移除
        in_degrees_zero.append(u)  # 存储入度为 0 的节点
        for v in graph[u]:
            in_degrees[v] -= 1  # 删除入度为 0 的节点,以及移除其指向
            if in_degrees[v] == 0:
                Q.append(v)
    return in_degrees_zero

if __name__ == '__main__':
    # 用字典的键值表示图的节点之间的关系,键为所有顶点。值是该顶点指向的顶点。
    graph_dict = {
        'a': 'bf',  # 表示 a 指向 b 和 f
        'b': 'cdf',
        'c': 'd',
        'd': 'ef',
        'e': 'f',
        'f': ''
    }

    t = topological_sort(graph_dict)
    print(t)

发表评论 取消回复
表情 图片 链接 代码

分享