如何在拓扑上对此数据结构进行排序

时间:2015-08-20 02:43:38

标签: c++ sorting dependencies

我正在玩弄一些依赖性的东西,我走得很远,但现在我被卡住了。

让我们说我有一个这样的数据结构:

map<string, vector<string>> deps;

其中映射中的每个键都是一个dependee节点,该键的值是dependee所依赖的节点列表。

此外,假设地图有4个键(A,B,C和D),并带有以下依赖项:

Dependencies

我正在寻找一种算法(一些拓扑排序?),它会产生一个字符串向量,使字符串按此顺序出现:

F, B, C, D, A
0  1  2  3  4

此列表表示应评估依赖项的顺序。

2 个答案:

答案 0 :(得分:1)

我最近根据this algorithm

提出了一个解决方案

这是您的数据结构的略微修改版本:

#include <map>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>

/**
 * Performs dependency resolution using
 * a topological sort
 */
template<typename ValueType>
class Resolver
{
public:
    using value_type = ValueType;
    using value_vec = std::vector<value_type>;
    using value_map = std::map<value_type, value_vec>;

private:
    value_vec seen;
    value_map deps;

    void resolve(value_type const& d, value_vec& sorted)
    {
        seen.push_back(d);
        for(auto const& nd: deps[d])
        {
            if(std::find(sorted.begin(), sorted.end(), nd) != sorted.end())
                continue;
            else if(std::find(seen.begin(), seen.end(), nd) == seen.end())
                resolve(nd, sorted);
            else
            {
                std::cerr << "Circular from " << d << " to " << nd << '\n';
                continue;
            }
        }
        sorted.push_back(d);
    }

public:

    /**
     * Clear the resolver ready for new
     * set of dependencies.
     */
    void clear()
    {
        seen.clear();
        deps.clear();
    }

    /**
     * Items that don't depend on anything
     */
    void add(value_type const& a)
    {
        deps[a];
    }

    /**
     * Item a depends on item b
     */
    void add(value_type const& a, value_type const& b)
    {
        deps[a].push_back(b);
    }

    value_vec resolve()
    {
        value_vec sorted;
        for(auto const& d: deps)
            if(std::find(sorted.begin(), sorted.end(), d.first) == sorted.end())
                resolve(d.first, sorted);
        return sorted;
    }
};

int main()
{
    Resolver<std::string> resolver;

    resolver.add("A", "B");
    resolver.add("A", "C");
    resolver.add("A", "D");

    resolver.add("B", "F");

    resolver.add("C", "B");
    resolver.add("C", "F");

    resolver.add("D", "C");

    resolver.add("F");

    for(auto const& d: resolver.resolve())
        std::cout << d << '\n';
}

<强>输出:

F
B
C
D
A

如果您发现任何错误(尚未经过良好测试),请告诉我。

从评论中添加:

  

为了提高效率,在生产代码中,如果节点类型(字符串,在此   例子)可以充满一个标志,将节点标记为已查看/已排序,   然后调用std :: find可以替换为设置   看到/排序的标志值。当然,在这个例子中,Galik   不能这样做,这就是使用std :: find的原因。 - @Dess

答案 1 :(得分:1)

如问题中所述,映射中的每个键都是一个从属节点,该键的值是依赖项所依赖的节点列表,或依赖项。

所以,实际的依赖关系就像(例如,'from - &gt; to'形式的数据集):

  1. B - &gt; A
  2. C - &gt; A
  3. D - &gt; A
  4. F - &gt;乙
  5. B - &gt; ç
  6. F - &gt; ç
  7. C - &gt; d
  8. 这个想法是先找到起点。起点永远不会在条目的“到”侧。一旦找到起点,我们就可以简单地遍历给定的集合以按顺序打印行程。以下是步骤。

    1. 在上面显示的表单中创建一组给定的条目(调用此数据集)。 “数据集”的每个条目的格式为“from-&gt; to”。
    2. 找到行程的起点。

      • 创建反向集。让反面为'reverseMap'。 “reverseMap”的条目具有“to-&gt; from”的形式。以下是上面例子的'reverseMap'。

        A -> B
        A -> C
        A -> D
        B -> F
        C -> B
        C -> F
        D -> C
        
      • 遍历'数据集'。对于数据集的每个键,检查它是否在“reverseMap”中表达式的左侧。如果没有钥匙,那么我们就找到了起点。在上面的例子中,“F”是起点。

      • 从上面找到起点并遍历'数据集'以打印行程,考虑以下规则。

        • 当我们遇到一个存在于数据集中多个'from'位置的节点时,检查表达式左侧reverseMap中所有'to'对应项的频率,并选择带有最低频率。

        例如,当我们以“F”作为起始节点开始时,我们在数据集中有两个选项向前推进,即“F - > B”和“F - &gt; C”。因此,我们检查表达式左侧的reverseMap中“C”和“B”的频率,结果分别为2和1。因此,我们继续前进B的条目(F - > B),并丢弃其他条目(F - > C)。此条目将来永远不会被要求。

    3. 我希望这会有所帮助。如果您发现任何错误,或者有什么要添加到此,请与我分享。