代码之家  ›  专栏  ›  技术社区  ›  justik

C++:STD::MAP,查找循环,算法

  •  0
  • justik  · 技术社区  · 7 年前

    假设以下数据结构:

    std::map <int, std::vector<int> > M, 
    

    其中,val由图的顶点序列表示,关键是序列的第一个顶点。例如

    {1} {1, 8, 12, 7}
    {4} {4, 3, 5}
    {7} {7, 9, 13, 18, 0, 2}
    {2} {2, 11, 1}
    {5} {5, 17, 10, 4}
    {9} {9, 6, 19, 14}
    {14} {14, 15, 9} 
    

    如何从段中查找所有循环(类似的起点和终点顶点)

    C1: {1 8 12 7} {7 9 13 18 0 2} {2 11 1}
    C2: {4 3 5} {5 17 10 4}
    C3: {9 6 19 14} {14, 15, 9} 
    

    以及如何避免片段的重复序列,时间复杂度低(地图可能包含数十万个序列)。任何循环都可以包含n个段,其中n>=1。

    这个 初始化 阶段:

    std::map <int, std::vector <int> > M;
    M[1] = std::vector<int>{ 1, 8, 12, 7 };
    M[4] = std::vector<int>{ 4, 3, 5 };
    M[7] = std::vector<int>{ 7, 9, 13, 18, 0, 2 };
    M[2] = std::vector<int>{ 2, 11, 1 };
    M[5] = std::vector<int>{ 5, 17, 10, 4 };
    M[9] = std::vector<int>{ 9, 6, 19, 14 };
    M[14] = std::vector<int>{ 14, 15, 9 };
    

    这个 草案 算法:

    std::vector<std::vector <int> > R;
    for (auto im = M.begin(); im != M.end();)
    {
        std::vector<int> r, ri = im->second;
        for(;;)
        {
            r.insert(r.end(), ri.begin(), ri.end());
            ri = M[r.back()];
            im = M.erase(M.find(im->first));
            if (r.back() == r.front()) break;
        }
        R.push_back(r);
    }
    

    不幸的是,重复删除意味着一个昂贵的操作…我希望有一个更漂亮、更有效的解决方案:—)

    谢谢你的帮助…

    2 回复  |  直到 7 年前
        1
  •  2
  •   Ben Voigt    7 年前

    首先,您的内部循环需要是一个函数(如果路径不循环呢?)

    然后,声明失败,如果

    • 结束节点的数值小于开始节点(可能是一个循环,但不是规范的,因此我们不会打印此移位版本)
    • 在主路径表中找不到结束节点

    这就产生了一个解决方案:

    bool try_follow(int from, std::vector<int>& result)
    {
        int current = from;
        while (true) {
            auto path = M.find(current);
            if (path == M.end()) return false;
            current = path->second.back();
            if (current < from) return false;
            result.insert(result.end(), path->second.begin()+1, path->second.end());
            if (current == from) return true;
        }
    }
    
    int main(void)  
    {
        for( auto& kvp : M )
        {
            std::vector<int> x;
            if (try_follow(kvp.first, x)) {
                std::cout << kvp.first;
                for( int y : x )
                    std::cout << " - " << y;
                std::cout << std::endl;
            }
        }
    }
    

    演示: https://rextester.com/DWWZ9457

        2
  •  -1
  •   Phil M    7 年前

    我的第一条裂缝:

    for (auto it : M)
    {
      if (it.first < it.second.back() && it.second.front() == M[it.second.back()].back())
        std::cout << "Cycle between " << it.first << " and " << it.second.back() << '\n';
    }
    

    当然,不会找到包含3+路径的循环。