加入收藏 | 设为首页 | 会员中心 | 我要投稿 济南站长网 (https://www.0531zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 教程 > 正文

拓扑排序的原理和达成

发布时间:2021-11-15 15:56:04 所属栏目:教程 来源:互联网
导读:定义 在图论中,由一个有向无环图组成的序列,只要满足下面两种情况则称为拓扑排序: 每个顶点只允许访问一次; 若顶点A在图中存在到达顶点B的路径,则不会存在顶点B到顶点A的路径,也就是说这条路径是单向的; 可以从这副图中发现,如果按照DFS的思想,那么

定义
在图论中,由一个有向无环图组成的序列,只要满足下面两种情况则称为拓扑排序:
 
每个顶点只允许访问一次;
若顶点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;
}
上面图的表现形式为邻接表,基本算法是用 BFS 和 DFS 来实现的;

(编辑:济南站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读