Topological Sort-拓扑排序
来源:互联网 发布:ios编程开发团队介绍 编辑:程序博客网 时间:2024/05/21 07:06
楔子
在枚舉所有排列的問題之中,如果我們另外再限制誰要排在誰前方、誰要排在誰後方,那麼在這些限制之下,合理的排列還會剩下哪些呢?
【註:枚舉所有排列,讀者們可另行參考「 Enumerate all n-tuples 」一文。】
先後限制與圖
誰要排在誰前方、誰要排在誰後方,其實就是兩兩之間的關係,故可以改用圖來表示:把圖上一條由 A 點連向 B 點的邊,想成是 A 必須排在 B 前方( B 必須排在 A 後方)。
當然啦,也可以把圖上一條由 A 點連向 B 點的邊,想成是 A 必須排在 B 後方。不過一般來說我們習慣成自然地使用前者。
Topological Sort 與 Topological Ordering
「拓樸排序」是排序一張有向圖的點的方式。把圖上一條由 A 點連向 B 點的邊,想成是 A 必須排在 B 前方( B 必須排在 A 後方)。 Topological Sort 用來找出合理的排列順序,讓每一個點的先後順序,滿足每一條邊所規定的先後順序。
「拓樸順序」是指一張有向圖經過「拓樸排序」後,每一個點的先後順序。
一張圖經過 Topological Sort 的結果可以有很多種。只要不違背圖上每一條邊的先後規定,要怎麼排列圖上的點都行。
圖上不能有環
當圖上有環時,便無法進行 Topological Sort 。因為環上每一個點都會有連向自己的邊,意味著環上每一個點必須排在其他點的後方,環上每一個點都不能在排列順序中拔得頭籌,所以合理的排列順序不存在。
觀察問題
要找出合理的排列順序,首先得決定第一點!知道如何找出第一點,那麼就可以循序漸進的再找出第二點、第三點了。
可以作為第一點的點,想必它不必排在其他點後方。也就是說,沒有被任何邊連向的點,就可以作為第一點。如果有很多個第一點,那麼找哪一點都行。
決定第一點之後,那麼剩下所有點都會在第一點後方。也就是說,由第一點連出去的邊,其先後規定已經被滿足了,規定存不存在都無所謂。因此,決定第一點之後,就可以刪去此點,以及刪去由此點連出去的邊──原問題可以遞迴地縮小!
只要反覆的尋找沒有被任何邊連向的點,然後刪去此點以及刪去由此點連出去的邊,就可以找出一個合理的排列順序了。
附帶一提,要找出合理的排列順序,也可以由最後一點開始決定!無論要從第一點找到最後一點,或是從最後一點找到第一點,都是可以的。各位可以想想看該怎麼做。
找出一個合理的排列順序( adjacency matrix )
儘管這個問題有 Recursive 的性質,可以用遞迴實作,但由於遞迴的分支只有一條,故亦可以用迴圈實做。我想大家都會選擇以比較簡單的迴圈方式來實做吧?
實作時可以利用變數紀錄圖上每一個點目前仍被多少條邊連到。尋找沒有被任何邊連向的點,就直接看該變數是不是零;刪去由此點連出去的邊,就順便更新變數的值。
- bool adj[9][9]; // adjacency matrix
- int ref[9]; // 紀錄圖上每一個點目前仍被多少條邊連到
- void topological_sort()
- {
- for (int i=0; i<9; ++i) ref[i] = 0; // 初始化為0
- // 累計圖上每一個點被幾條邊連到
- for (int i=0; i<9; ++i)
- for (int j=0; j<9; ++j)
- if (adj[i][j])
- ref[j]++;
- // 開始找出一個合理的排列順序
- for (int i=0; i<9; ++i)
- {
- // 尋找沒有被任何邊連向的點
- int s = 0;
- while (s < 9 && ref[s] != 0) ++s;
- if (s == 9) break; // 找不到。表示目前殘存的圖是個環。
- ref[s] = -1; // 設為已找過(刪去s點)
- cout << s; // 印出合理的排列順序的第i點
- // 更新ref的值(刪去由s點連出去的邊)
- for (int t=0; t<9; ++t)
- if (adj[s][t])
- ref[t]--;
- }
- }
找出一個合理的排列順序( adjacency lists )
- int adj[9][9], size[9]; // adjacency lists
- int ref[9]; // 紀錄圖上每一個點目前仍被多少條邊連到
- void topological_sort()
- {
- for (int i=0; i<9; ++i) ref[i] = 0; // 初始化為0
- // 累計圖上每一個點被幾條邊連到
- for (int i=0; i<9; ++i)
- for (int j=0; j<size[i]; ++j)
- ref[adj[i][j]]++;
- // 宣告一個queue來紀錄已經沒有被任何邊連向的點
- queue<int> Q;
- for (int i=0; i<9; ++i)
- if (ref[i] == 0)
- Q.push(i);
- // 開始找出一個合理的排列順序
- for (int i=0; i<9; ++i)
- {
- // 尋找沒有被任何邊連向的點
- if (Q.empty()) break; // 找不到。表示目前殘存的圖是個環。
- int s = Q.front(); Q.pop();
- ref[s] = -1; // 設為已找過(刪去s點)
- cout << s; // 印出合理的排列順序的第i點
- // 更新ref的值(刪去由s點連出去的邊)
- for (int j=0; j<size[s]; ++j)
- {
- int t = adj[s][j];
- ref[t]--;
- if (!ref[t]) Q.push(t); // 紀錄已經沒有被任何邊連向的點
- }
- }
- }
時間複雜度
時間複雜度等於一次 Graph Traversal 的時間。圖的資料結構為 adjacency matrix 的話,便是 O(V^2) ;圖的資料結構為 adjacency lists 的話,便是 O(V+E) 。
UVa 10305 200
找出所有合理的排列順序
請用 backtracking 。此處不詳述了,直接看練習題吧。
UVa 124
計算所有合理的排列順序個數
需要使用 Dynamic Programming 解決,時間複雜度 O(2^V * V^2) 。
- Topological Sort-拓扑排序
- 拓扑排序(Topological Sort)
- 拓扑排序 Topological-sort
- 拓扑排序 - Topological Sort
- Topological Sort拓扑排序
- 拓扑排序(Topological Sort)
- 拓扑排序(Topological Sort)
- 拓扑排序(topological-sort)
- 拓扑排序(topological sort)
- 拓扑排序(topological sort)
- 拓扑排序(Topological Sort)
- topological-sort(拓扑排序)
- 拓扑排序(topological sort)DFS
- DSOJ Topological Sort(拓扑排序)
- 拓扑排序学习(Topological Sort)
- 图论算法:拓扑排序(Topological Sort)
- pta ——Topological Sort(拓扑排序)
- 有关Topological-sort(拓扑排序)的belabela...
- C++多继承构造函数执行顺序
- 观看公开课“我是我的连接体”--自我发现
- 详解 JavaScript 回调函数
- 关于采用matlab进行指定非线性方程拟合的问题
- Ubuntu中文编码配置
- Topological Sort-拓扑排序
- linux shell编程之菜单选择(二)
- C#中值传递与引用传递的区别 .
- 搭建了个CooCooWakka维基站点
- JavaScript跨域总结与解决办法
- HDOJ2029 Palindromes _easy version 回文数
- Java复习笔----类对象定义及存储原理
- C#委托与事件
- CCScene切换的所有特效(28种)以及设置屏幕横竖屏!