图的拓扑排序(C++实现)

#ifndef TOPO_H
#define TOPO_H

#include
#include
#include
#include
#include
#include
#include"../data_struct/data_struct.h"

/// 拓扑排序
/// 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序
/// 是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前
/// 我们把这种顶点表示活动、边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简称AOV网
/// 一个AOV网应该是一个有向无环图,即不应该带有回路,因为若带有回路,则回路上的所有活动都无法进行(对于数据流来说就是死循环)。
/// 在AOV网中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面,
/// 我们把此序列叫做拓扑序列(Topological order),由AOV网构造拓扑序列的过程叫做拓扑排序(Topological sort)。
/// AOV网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。
/// 二者的复杂度均为O(V+E)
class graph_topo
{
public:
    /// Kahn(卡恩)算法:我们先使用一个栈保存入度为0 的顶点,然后输出栈顶元素并且将和栈顶元素有关的边删除,
    /// 减少和栈顶元素有关的顶点的入度数量并且把入度减少到0的顶点也入栈
    static bool kahn(const std::shared_ptr& graph, std::vector& result)
    {
        auto edges = get_adj_node(graph);
        std::vector indgree(graph->vertex.size(), 0);

        for (auto& edge : edges)
        {
            for (auto v : edge.second)
            {
                indgree[v]++;
            }
        }

        std::queue q0; // 用来存储入度为0的顶点
        for (uint32_t i = 0; i < graph->vertex.size(); i++)
        {
            if (indgree[i] == 0)
            {
                q0.push(i);
            }
        }

        while (!q0.empty())
        {
            auto u = q0.front();
            q0.pop();
            result.push_back(u);

            if (edges.end()== edges.find(u))
            {
                continue;
            }

            // 遍历由u引出的所有边
            for(auto v : edges[u])
            {
                indgree[v]--;
                if (indgree[v] == 0)
                {
                    q0.push(v);
                }
            }

            edges.erase(u);
        }

        if (edges.size() != 0) // 如果此时图中还存在边,那么说明图中含有环路
        {
            return false;
        }
        return true;
    }

    /// 其实DFS就是深度优先搜索,它每次都沿着一条路径一直往下搜索,知道某个顶点没有了出度时,就停止递归,往回走,
    /// 所以我们就用DFS的这个思路,我们可以得到一个有向无环图的拓扑序列,其实DFS很像Kahn算法的逆过程。
    static bool dfs_r(const std::shared_ptr& graph, std::vector& result)
    {
        //用 -1 来表示顶点正在访问
        std::vector is_visited(graph->vertex.size(), 0);
        auto edges = get_adj_node(graph);

        for (auto i: graph->vertex)
        {
            if (is_visited[i] == 0)
            {
                if (!_dfs_r(i, is_visited, edges, result))
                    return false; //有环
            }
        }

        std::reverse(result.begin(), result.end());

        return true;
    }

private:
    static bool _dfs_r(int u, std::vector& is_visited,
                     std::unordered_map>& edges,
                     std::vector& result)
    {
        is_visited[u] = -1; //用来表示顶点正在访问
        if (edges.end()!= edges.find(u))
        {
            for(auto v : edges[u])
            {
                if (-1 == is_visited[v]) //表示这个点进入了两次,肯定出现了环
                {
                    return false;
                }

                if (is_visited[v] == 0)
                {
                    if (!_dfs_r(v, is_visited, edges, result))
                    {
                        return false;
                    }
                }
            }
        }

        is_visited[u] = true;

        result.push_back(u); //放到结果数组里,输出的时候记得倒序输出,(回溯的原因)
        return true;
    }

    /// 相邻的节点
    static std::unordered_map> get_adj_node(
            const std::shared_ptr& graph)
    {
        std::unordered_map> edges;

        for (auto i: graph->vertex)
        {
            for (auto j : graph->vertex)
            {
                if (i != j && graph->matrix[i][j] < INT_MAX)
                {
                    if (edges.end() != edges.find(i))
                    {
                        edges[i].insert(j);
                    }
                    else
                    {
                        std::unordered_set s;
                        s.insert(j);
                        edges.insert({i, s});
                    }
                }
            }
        }

        return edges;
    }
};

#endif // TOPO_H

 

你可能感兴趣的:(算法)