【问题标题】:Topological Sort with Grouping带分组的拓扑排序
【发布时间】:2011-05-03 15:41:55
【问题描述】:

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

假设以下数据: a -> bc -> da 必须在 b 之前,c 必须在 d 之前)。
只有这两个约束,我们有多个候选解决方案:(a b c da c d bc a b d 等)。但是,我希望创建一种“分组”这些节点的方法,以便在处理一个组之后,下一个组中的所有条目都可以处理它们的依赖关系。对于上述假设的数据,我会寻找像(a, c) (b, d) 这样的分组。在每个组中,处理节点的顺序无关紧要(ac 之前或bd 之前,等等,反之亦然)只要组 1 (a, c) 在任何组之前完成2 个(b, d) 已处理。

唯一的附加问题是每个节点都应该在最早的组中。考虑以下情况:
a -> b -> c
d -> e -> f
x -> y

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

关于如何进行此操作的任何想法?


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

// 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;
}

【问题讨论】:

  • 听起来你只是想要一个“贪婪”的分组。查找可以在第一组中的所有节点。然后找到所有可以在第二组中的节点,依此类推,直到没有节点未分配。
  • 感谢您发布您的解决方案,但您能否更好地描述预期的输入格式?也许举个例子?
  • @mpen - 我发布的解决方案需要一个二维向量邻接矩阵。我会包含原始输入格式,但这个问题已经有将近 6 年的历史了,而且我已经放错了原始代码。
  • @Mr.Llama 哦..我想我明白了。这与我在其他拓扑排序实现中看到的格式不同,但可以。

标签: java php c++ graph graph-theory


【解决方案1】:

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

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

【讨论】:

  • 有点像从每个根节点开始的广度优先搜索,只有当它的值更新为更大的数字时才会处理一个子节点? (顺便说一句,如果我改为进行深度优先搜索,这有关系吗?)
  • 哪些节点是根节点? “将它们传播给孩子”是否意味着在图形的同一区域上多次通过?
  • 我认为广度优先搜索在这里更合适。但是,如果您发现 DFS 更容易实现。如果你正在处理一个小图(最多几百或几千个节点),那么任何一种方法都可以。
  • Andy:它在概念上传播。一个好的实现将避免( BFS 与 DFS )将大大减少多次遍历同一区域。根是没有依赖关系的节点。
  • 后续问题:使用该算法,应该如何检查图中是否存在循环(不会显着恶化时间或内存复杂度)?
【解决方案2】:

我最近实现了这个算法。我从您展示的方法开始,但它没有扩展到 20+ 百万个节点的图表。我最终得到的解决方案是基于the approach detailed here

你可以把它想象成计算每个节点的高度,然后结果是给定高度的每个节点的一组。

考虑图表:

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)

当你访问一个节点时,减少它指向的每个节点的数量,如果这个数字变为零,你就知道该节点在下一个高度。

【讨论】:

    猜你喜欢
    • 2012-02-08
    • 1970-01-01
    • 2023-03-16
    • 2021-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多