带分组的拓扑排序

2022-08-30 22:27:40

好吧,所以在拓扑排序中,根据输入数据,通常有多个正确的解决方案,可以“处理”图形的顺序,以便所有依赖项都位于“依赖”它们的节点之前。但是,我正在寻找一个稍微不同的答案:

假设以下数据:和 ( 必须位于之前,并且必须位于 之前)。
仅凭这两个约束,我们就有多个候选解决方案:(、 、 等)。但是,我希望创建一种“分组”这些节点的方法,以便在处理组之后,下一组中的所有条目都得到处理。对于上述假定的数据,我会寻找一个像.在每个组中,节点的处理顺序(在或之前,依此类推)并不重要,只要组 1 在处理组 2 中的任何一个之前完成即可。a -> bc -> dabcda b c da c d bc a b d(a, c) (b, d)acbd(a, c)(b, d)

唯一的额外问题是每个节点都应该在尽可能早的组中。请考虑以下事项:
a -> b -> c
d -> e -> f
x -> y

分组方案在技术上是正确的,因为 是 之前,更优化的解决方案是因为在组 2 中意味着它依赖于组 1 中的某个节点。(a, d) (b, e, x) (c, f, y)xy(a, d, x) (b, e, y) (c, f)xx

关于如何做到这一点的任何想法?


编辑:我想我设法拼凑了一些解决方案代码。感谢所有帮助过的人!

// Topological sort
// Accepts: 2d graph where a [0 = no edge; non-0 = edge]
// Returns: 1d array where each index is that node's group_id
vector<int> top_sort(vector< vector<int> > graph)
{
    int size = graph.size();
    vector<int> group_ids = vector<int>(size, 0);
    vector<int> node_queue;

    // Find the root nodes, add them to the queue.
    for (int i = 0; i < size; i++)
    {
        bool is_root = true;

        for (int j = 0; j < size; j++)
        {
            if (graph[j][i] != 0) { is_root = false; break; }
        }

        if (is_root) { node_queue.push_back(i); }
    }

    // Detect error case and handle if needed.
    if (node_queue.size() == 0)
    {
        cerr << "ERROR: No root nodes found in graph." << endl;
        return vector<int>(size, -1);
    }


    // Depth first search, updating each node with it's new depth.
    while (node_queue.size() > 0)
    {
        int cur_node = node_queue.back();
        node_queue.pop_back();

        // For each node connected to the current node...
        for (int i = 0; i < size; i++)
        {
            if (graph[cur_node][i] == 0) { continue; }

            // See if dependent node needs to be updated with a later group_id
            if (group_ids[cur_node] + 1 > group_ids[i])
            {
                group_ids[i] = group_ids[cur_node] + 1;
                node_queue.push_back(i);
            }
        }
    }

    return group_ids;
}

答案 1

用级别值 0 标记所有根节点。用级别值 parent+1 标记所有子级。如果正在重新访问某个节点,即它已经分配了一个级别值,请检查先前分配的值是否低于新值。如果是这样,请使用较高的值对其进行更新,并将其传播给后代。

现在,您拥有与唯一级别标签一样多的组 0 ...K


答案 2

我最近实现了这个算法。我从您展示的方法开始,但它没有扩展到2000多万个节点的图形。我最终得到的解决方案是基于这里详述的方法

您可以将其视为计算每个节点的高度,然后结果是给定高度下每个节点的一组。

考虑下图:

A -> X

B -> X

X -> Y

X -> Z

因此,所需的输出是(A,B),(X),(Y,Z)

基本方法是找到没有使用它的所有内容(在本例中为A,B)。所有这些都在高度为0。

现在从图表中删除A和B,找到任何现在没有使用它的东西(现在在这个例子中是X)。所以 X 的高度为 1。

从图形中删除X,找到现在没有使用它的任何内容(在本例中为Y,Z)。所以Y,Z在高度2。

您可以通过意识到您不需要为所有内容存储双向边或实际从图形中删除任何内容来进行优化,您只需要知道指向节点的事物的数量以及您知道的节点处于下一个高度。

因此,对于开头的此示例:

  • 0 事物使用 1
  • 0 事物使用 2
  • 2 事物使用 X (1 和 2)
  • 1 事物使用 Y,Z (X)

当您访问一个节点时,请减少它所指向的每个节点的数量,如果该数字变为零,则您知道该节点位于下一个高度。


推荐