拓扑排序详解------转

来源:互联网 发布:冒泡排序法的代码c语言 编辑:程序博客网 时间:2024/06/03 20:20

转载来自点击打开链接

以下将文章的一些内容粘贴过来以方便自己查看,请尊重原创,转载请注明

假设我们有一组任务要完成,并且有些任务要在其它任务完成之后才能开始,所以我们必须非常小心这些任务的执行顺序。
如果这些任务的执行顺序足够简单的话,我们可以用链表来存储它们,这是一个很好的方案,让我们可以准确知道任务的执行顺序。问题是有时候不同任务之间的关系是非常复杂的,有些任务依赖于两个甚至更多的任务,或者反过来很多任务依赖自己。
因此我们不能通过链表或者树的数据结构来对这个问题建模。对这类问题唯一合理的数据结构就是图。我们需要哪种图呢?很显然,我们需要有向图来描述这种关系,而且是不能循环的有向图,我们称之为有向无环图。要通过拓扑排序对图形进行排序,这些图必须是不能循环和有向的。为什么这些图不能循环呢?答案很明显,如果图形是循环的,我们无法知道哪个任务该优先执行,也不可能对任务进行排序。现在我们一要做的是对图中的每个节点排序,组成一条条边(u,v),u在v之前执行。然后我们就可以得到所有任务的线性顺序,并按这种顺序执行任务就一切都OK了。

例如,下面的图的一个拓扑排序是“5 4 2 3 1 0”。一个图可以有多个拓扑排序。
另一个拓扑排序是“4 5 2 3 1 0”。拓扑排序的第一个顶点总是入度为0。

graph

方法一
现在我们可以得到这个算法的基本步骤:

11.构造空列表 L和S;
22.把所有没有依赖节点(入度为0)的节点放入L;
33.当L还有节点的时候,执行下面步骤:
43.1     L中拿出一个节点n(从L中remove掉),并放入S
53.2         对每一个邻近n的节点m,
63.2.1           去掉边(n,m);(表示加入最终结果集S)
73.2.2           如果m没有依赖节点(入度为零),把m放入L;

核心就是:每次都选取入度为0的节点,再更新其相邻的节点的入度 。

这个是相对比较直观的算法,也是常见的一种算法。我们用一个数组degree[]记录所有顶点的入度。删除点时更新该数组。
参考下面代码函数:topologicalSort1()

方法二
另外一种方法是参考DFS,对图的深度优先遍历做些修改。我们确信在有向图中如果存在一条边(u,v),那么顶点u会先于顶点v进入列表中。因此在深度遍历时,用栈来存储遍历的顺序。参考下面代码的函数:topologicalSort2()

C++实现

view source
001// C++实现的拓扑排序算法
002#include<iostream>
003#include <list>
004#include <stack>
005using namespace std;
006 
007// 图类
008class Graph
009{
010    int V;    //顶点个数
011 
012    // 邻接表
013    list<int> *adj;
014    // 拓扑排序方法 2的辅助函数
015    void topologicalSortRecall(int v, bool visited[], stack<int> &Stack);
016 
017public:
018    Graph(int V);
019 
020     // 添加边
021    void addEdge(int v, int w);
022 
023    //拓扑排序普通方法
024    void topologicalSort1();
025 
026    // 拓扑排序方法二
027    void topologicalSort2();
028};
029 
030Graph::Graph(int V)
031{
032    this->V = V;
033    adj = new list<int>[V];
034}
035 
036void Graph::addEdge(int v, int w)
037{
038    adj[v].push_back(w); //
039}
040 
041//类似深度优先遍历,将和V相邻的顶点(且为访问过的)放入栈中
042void Graph::topologicalSortRecall(int v, bool visited[], stack<int> &stk)
043{
044    //标记v为访问过的
045    visited[v] = true;
046 
047    // 对每个顶点进行递归调用
048    list<int>::iterator i;
049    for (i = adj[v].begin(); i != adj[v].end(); ++i)
050        if (!visited[*i])
051            topologicalSortRecall(*i, visited, stk);
052 
053    // 保存顶点
054    stk.push(v);
055}
056 
057// 方法二,使用递归调用实现拓扑排序
058void Graph::topologicalSort2()
059{
060    stack<int> stk;
061    bool *visited = new bool[V];
062    for (int i = 0; i < V; i++)
063        visited[i] = false;
064 
065    //每个顶点都调用一次
066    for (int i = 0; i < V; i++)
067      if (visited[i] == false)
068        topologicalSortRecall(i, visited, stk);
069 
070    // 打印
071    while (stk.empty() == false)
072    {
073        cout << stk.top() << " ";
074        stk.pop();
075    }
076}
077 
078// 方法一
079void Graph::topologicalSort1()
080{
081    list<int>::iterator j;
082    int degree[V];
083    //遍历所有的边,计算入度
084    for(int i=0; i<V; i++){
085        degree[i] = 0;
086        for (j = adj[i].begin(); j != adj[i].end(); ++j){
087            degree[*j]++;
088        }
089    }
090    list<int> zeroNodes;//所有入度为0的点
091    list<int> result;//所有入度为0的点
092    for(int i=0; i<V; i++){
093        if(degree[i] == 0){
094            zeroNodes.push_back(i);
095        }
096    }
097    while(zeroNodes.size() > 0){
098        int top = zeroNodes.back();
099        zeroNodes.pop_back();
100        result.push_back(top);
101        for (j = adj[top].begin(); j != adj[top].end(); ++j){
102            degree[*j]--;//删除和top相邻的边,并更新其它顶点的入度
103            if(degree[*j] == 0) zeroNodes.push_back(*j);
104        }
105    }
106 
107    //打印结果
108    for(j= result.begin(); j != result.end(); j++)
109        cout << (*j) << " ";
110}
111 
112int main()
113{
114    // 创建文中所以的图
115    Graph g(6);
116    g.addEdge(5, 2);
117    g.addEdge(5, 0);
118    g.addEdge(4, 0);
119    g.addEdge(4, 1);
120    g.addEdge(2, 3);
121    g.addEdge(3, 1);
122 
123    cout << "Following is a Topological Sort of the given graph using  topologicalSort1\n";
124    g.topologicalSort1();
125    cout << endl;
126    cout << "Following is a Topological Sort of the given graph using  topologicalSort2\n";
127    g.topologicalSort2();
128    return 0;
129}

0 0