具有优先级节点的图的拓扑排序

时间:2018-09-15 22:24:56

标签: python graph

这是我的问题,我有一个非常简单的graph

或者当我用python编写脚本时,这里是作为字典(子作为键,父作为值):

{'b':'a', 'c':'a', 'd':'c', 'e':'c'}

现在,我知道,使用出色的toposort模块,我可以将此图展开到一个列表中,这样从第一个开始访问列表中的每个节点将尊重父母与孩子之间的图关系。效果很好,在这种情况下,我会得到:

['a', 'b', 'c', 'd', 'e']

现在的问题是,在我的情况下,每个节点都有一个优先级,该优先级告诉我在所有父节点都已被访问之前应该访问哪个节点。这是优先级的示例dict(键是节点,值是优先级,优先级越高越重要):

{'a':1, 'b':2, 'c':3, 'e':4, 'd':1} 

所以我想有一个算法可以吐出这样的内容:

['a', 'c', 'e', 'b', 'd']

或任何其他等效的解决方案(嗯,我猜解决方案将不是唯一的,但我可能错了)。换句话说,我需要首先考虑节点之间的关系来定义链,然后再确定优先级。

这里有什么我可以使用的吗?或者我该如何利用python中的toposort模块来实现这一目标?

谢谢!

1 个答案:

答案 0 :(得分:0)

您可以使用heap来实现拓扑排序以处理优先级。 Python提供了heapq module来处理堆。

Kahn's algorithm启发的实现可以如下:

import heapq


def toposort(graph, key=None):
    """
    The functions returns the topological order of the graph

    :param graph: A dictionary where the keys are the node labels and the values are a list of the neighbors labels
    :param key: A priority for each node
    :return: The topological order of the graph
    """

    # init the indegree for each noe
    nodes = graph.keys() | set([node for adjacents in graph.values() for node in adjacents])
    in_degree = {node: 0 for node in nodes}

    # compute the indegree
    for k, adjacents in graph.items():
        for node in adjacents:
            in_degree[node] += 1

    # init the heap with the nodes with indegree 0 and priority given by key
    heap = [(key(node), node) for node, degree in in_degree.items() if degree == 0]
    heapq.heapify(heap)

    top_order = []
    while heap:  # heap is not empty
        _, node = heapq.heappop(heap)  # get the element with highest priority and remove from heap
        top_order.append(node)  # add to topological order
        for adjacent in graph.get(node, []):  # iter over the neighbors of the node
            in_degree[adjacent] -= 1
            if in_degree[adjacent] == 0:  # if the node has in_degree 0 add to the heap with priority given by key
                heapq.heappush(heap, (key(adjacent), adjacent))

    return top_order

注释: 根据文档中的指定,算法会收到一个图形,其中的键是节点标签,值是节点标签的列表,因此您需要反转图形,其次,堆是最小堆,因此您还需要反转优先级(乘以-1)。最终代码如下所示:

g = {'b': 'a', 'c': 'a', 'd': 'c', 'e': 'c'}
reverse = {}
for k, v in g.items():
    if v not in reverse:
        reverse[v] = []
    reverse[v].append(k)

table = {'a': -1, 'b': -2, 'c': -3, 'e': -4, 'd': -1}
order = toposort(reverse, table.get)
print(order)

输出

['a', 'c', 'e', 'b', 'd']

请注意,调用order = toposort(reverse, table.get)是对上面定义的函数toposort的调用。