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

转载来自点击打开链接

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

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

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

graph

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

1 1.构造空列表 L和S;
2 2.把所有没有依赖节点(入度为0)的节点放入L;
3 3.当L还有节点的时候,执行下面步骤:
4 3.1     L中拿出一个节点n(从L中remove掉),并放入S
5 3.2         对每一个邻近n的节点m,
6 3.2.1           去掉边(n,m);(表示加入最终结果集S)
7 3.2.2           如果m没有依赖节点(入度为零),把m放入L;

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

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

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

C++实现

001 // C++实现的拓扑排序算法
002 #include<iostream>
003 #include <list>
004 #include <stack>
005 using namespace std;
006  
007 // 图类
008 class Graph
009 {
010     int V;    //顶点个数
011  
012     // 邻接表
013     list<int> *adj;
014     // 拓扑排序方法 2的辅助函数
015     void topologicalSortRecall(int v, bool visited[], stack<int> &Stack);
016  
017 public:
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  
030 Graph::Graph(int V)
031 {
032     this->V = V;
033     adj = new list<int>[V];
034 }
035  
036 void Graph::addEdge(int v, int w)
037 {
038     adj[v].push_back(w); //
039 }
040  
041 //类似深度优先遍历,将和V相邻的顶点(且为访问过的)放入栈中
042 void 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 // 方法二,使用递归调用实现拓扑排序
058 void 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 // 方法一
079 void 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  
112 int 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 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值