• 拓扑排序的原理和实现


    定义

    在图论中,由一个有向无环图组成的序列,只要满足下面两种情况则称为拓扑排序:

    • 每个顶点只允许访问一次
    • 若顶点A在图中存在到达顶点B的路径,则不会存在顶点B到顶点A的路径,也就是说这条路径是单向的;

    可以从这副图中发现,如果按照DFS的思想,那么其访问结点的结果为 5,2,3,1,0,4,但是如果是拓扑排序的话,访问结点的结果为5,4,2,0,1,3,类似于多叉树的BFS

    问题

    拓扑排序可用来解决什么问题呢?比如说课程排序,编译依赖,类似凡是涉及到相关顺序的时间安排;还可以用来判断一幅有向图是否无环。

    思路

    根据前面提供的思想,首先想到的就是BFS,但是需要在BFS的基础上进行判断,只有入度为0的结点才能加入到队列中,其中每访问一个结点,则将该结点的入度减一。(因为多叉树的结点不可能存在环,所以其的BFS就不用担心入度的问题)

    如果是按照DFS的思想,则需要在等待迭代完结点的连接邻接点后再把当前结点压入栈中。

    实现

    #include <iostream>
    #include <list>
    #include <stack>
    #include <queue>
    #include <vector>
    using namespace std;
    
    /**
     * 构建邻接矩阵
     */
    class Graph {
    public:
        Graph(int v);
        ~Graph();
    
        list<int>* adj; //邻接表
        vector<int> nums; //统计结点的入度
    
        int V; //顶点数目
    
        // 添加一条从start为起始点,end为终点的边
        void addEdge(int start, int end);
        void bfs_topological_sort();
        void dfs_topological_sort();
    
    protected:
        void _bfs_topological_sort(vector<int> &result, queue<int>& queue);
        void _dfs_topological_sort(int v, bool visited[], stack<int>& stack);
    };
    
    Graph::Graph(int v) : V(v) {
        adj = new list<int>[V];
        nums.assign(v, 0);
    }
    
    Graph::~Graph() {
    
    }
    
    void Graph::addEdge(int start, int end) {
        this->adj[start].push_back(end);
        this->nums[end]++;
    }
    
    void Graph::_bfs_topological_sort(vector<int> &result, queue<int> &queue) {
        while (!queue.empty()) {
            int v = queue.front();
            queue.pop();
    
            result.push_back(v);
            for (auto itr = this->adj[v].begin(); itr != this->adj[v].end(); ++itr) {
                this->nums[*itr]--; //*itr结点的入度减1,也就是说删掉 v 到 *itr 的有向边
                if (!this->nums[*itr]) {
                    queue.push(*itr);
                }
            }
        }
    
        // 判断是否有环(正常情况下所有结点此时的入度都为0)
        for (int i = 0; i < V; i++) {
            if (this->nums[i] != 0) {
                cout << "have a ring" << endl;
            }
        }
    }
    
    void Graph::bfs_topological_sort() {
        queue<int> queue;
    
        // 计算所有的入度为0的结点,作为起点
        for (int i = V - 1; i >= 0; i--) {
            if (!this->nums[i]) {
                queue.push(i);
            }
        }
    
        vector<int> result;
        this->_bfs_topological_sort(result, queue);
    
        for (auto itr = result.begin(); itr != result.end(); itr++) {
            cout << *itr << "->";
        }
    }
    
    void Graph::_dfs_topological_sort(int v, bool *visited, stack<int> &stack) {
        if (visited[v]) return ;
    
        visited[v] = true;
        for (auto itr = this->adj[v].begin(); itr != this->adj[v].end(); ++itr) {
            if (!visited[*itr]) {
                _dfs_topological_sort(*itr, visited, stack);
            }
            else {
                // 判断是否有环,不用可以删掉(用邻接矩阵表示更简单,临街表还是需要遍历才能知道是否两点之间是否连接)
                for (auto vitr = this->adj[*itr].begin(); vitr != this->adj[*itr].end(); ++vitr) {
                    if (*vitr == v) {
                        cout << "ring is " << *itr << "->" << v << endl;
                    }
                }
            }
        }
    
        // 访问完结点所有的临街结点之后才加入到栈中
        stack.push(v);
    }
    
    void Graph::dfs_topological_sort() {
        stack<int> stack;
        bool visited[V];
        memset(visited, false, sizeof(visited));
    
        for (int i = V - 1; i >= 0; i--) {
            if (!visited[i]) {
                _dfs_topological_sort(i, visited, stack);
            }
        }
    
        while (!stack.empty()) {
            int v = stack.top();
            stack.pop();
            cout << v << "->";
        }
    };
    
    int main() {
    
        // 构建邻接矩阵
        Graph g(6); //6个结点
        g.addEdge(5, 2);
        g.addEdge(5, 0);
        g.addEdge(4, 0);
        g.addEdge(4, 1);
        g.addEdge(2, 3);
        g.addEdge(3, 1);
        g.addEdge(1, 3);
    
        g.bfs_topological_sort();
        g.dfs_topological_sort();
    
        return 0;
    }
    
    

    上面图的表现形式为邻接表,基本算法是用 BFSDFS 来实现的;

  • 相关阅读:
    agc015D A or...or B Problem
    agc016E Poor Turkeys
    CTSC2016时空旅行
    假期的宿舍[ZJOI2009]
    上白泽慧音(luogu P1726
    小K的农场(luogu P1993
    Cashier Employment(poj1275
    Intervals(poj1201
    序列分割[Apio2014]
    特别行动队[APIO2010]
  • 原文地址:https://www.cnblogs.com/George1994/p/6673064.html
Copyright © 2020-2023  润新知