拓扑排序
2015-07-11 23:10
225 查看
在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序(Topological sorting)。
1)每个顶点出现且只出现一次;
2)若A在序列中排在B的前面,则在图中不存在从B到A的路径。
也可以定义为:拓扑排序是对有向无环图的顶点的一种排序,它使得如果存在一条从顶点A到顶点B的路径,那么在排序中B出现在A的后面。
什么地方会用到拓扑排序呢?比如在存在着前后依赖关系的情况下,整个工程中,有些子工程(活动)必须在其它有关子工程完成之后才能开始,也就是说,一个子工程的开始是以它的所有前序子工程的结束为先决条件的,但有些子工程没有先决条件,可以安排在任何时间开始。
举个例子:学习《数据结构》课程就必须安排在学完它的两门先修课程《离散数学》和《算法语言》之后。学习《高等数学》课程则可以随时安排,因为它是基础课程,没有先修课。
拓扑排序是对于有向无环图而言的(DAG),就是对于这个图所有的点(V1, V2, … Vn)找到一个点序列使得任意边(u, v), u出现在v的前面。很容易证明,如果一个有向图中有环那么不存在拓扑排序。
1)从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。
2)从图中删除该顶点和所有以它为起点的有向边。
3)重复 1 和 2 直到当前的 DAG 图为空或当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。
……
复杂度分析:
§Initialize In-Degree array:
O(|E|)
§Find vertex with in-degree 0:
|V| vertices, each takes O(|V|) to search In-Degree array.
Total time = O(|V|^2)
§Reduce In-Degree of all vertices adjacent to a vertex:
O(|E|)
§Output and mark vertex:
O(|V|)
Total time = O(|V|^2 + |E|) Quadratic time!
Kahn算法:
Total time = O(|V| + |E|)
DFS:
深度优先方法,其实可看做逆向的方法,找到没有出度的点,通过递归逆推。
L ← Empty list that will contain the sorted nodes
S ← Set of all nodes with no outgoing edges
for each node n in S do
visit(n)
function visit(node n)
if n has not been visited yet then
mark n as visited
for each node m with an edge from m to n do
visit(m)
add n to L
参考:
https://zh.wikipedia.org/wiki/%E6%8B%93%E6%92%B2%E6%8E%92%E5%BA%8F
http://www.ivy-end.com/archives/992
1)每个顶点出现且只出现一次;
2)若A在序列中排在B的前面,则在图中不存在从B到A的路径。
也可以定义为:拓扑排序是对有向无环图的顶点的一种排序,它使得如果存在一条从顶点A到顶点B的路径,那么在排序中B出现在A的后面。
什么地方会用到拓扑排序呢?比如在存在着前后依赖关系的情况下,整个工程中,有些子工程(活动)必须在其它有关子工程完成之后才能开始,也就是说,一个子工程的开始是以它的所有前序子工程的结束为先决条件的,但有些子工程没有先决条件,可以安排在任何时间开始。
举个例子:学习《数据结构》课程就必须安排在学完它的两门先修课程《离散数学》和《算法语言》之后。学习《高等数学》课程则可以随时安排,因为它是基础课程,没有先修课。
拓扑排序是对于有向无环图而言的(DAG),就是对于这个图所有的点(V1, V2, … Vn)找到一个点序列使得任意边(u, v), u出现在v的前面。很容易证明,如果一个有向图中有环那么不存在拓扑排序。
1)从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。
2)从图中删除该顶点和所有以它为起点的有向边。
3)重复 1 和 2 直到当前的 DAG 图为空或当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。
……
复杂度分析:
§Initialize In-Degree array:
O(|E|)
§Find vertex with in-degree 0:
|V| vertices, each takes O(|V|) to search In-Degree array.
Total time = O(|V|^2)
§Reduce In-Degree of all vertices adjacent to a vertex:
O(|E|)
§Output and mark vertex:
O(|V|)
Total time = O(|V|^2 + |E|) Quadratic time!
Kahn算法:
Total time = O(|V| + |E|)
#include <iostream> #include <list> #include <queue> #include <vector> using namespace std; class Graph{ int V; //定点数 list<int>* adj; //邻接表数组指针 public: Graph(int v); void AddEdge(int v, int w); int GetVecNum(); list<int>* GetAdj(); }; Graph::Graph(int v):V(v){ adj = new list<int>[v]; }; void Graph::AddEdge(int v, int w){ adj[v].push_back(w); } int Graph::GetVecNum(){ return V; } list<int>* Graph::GetAdj(){ return adj; } const int V = 6; queue<int> zeroInDegreesQueue; int inDegrees[V]; vector<int> resultVec; void InitZeroIndegreeQueue(Graph& graph){ list<int>* adj = graph.GetAdj(); for(int i=0; i<graph.GetVecNum(); i++){ for (list<int>::iterator it=adj[i].begin(); it != adj[i].end(); ++it) inDegrees[*it]++; } for(int i=0; i<graph.GetVecNum(); i++){ if(inDegrees[i] == 0) zeroInDegreesQueue.push(i); } } void KahnTopologicalSort(Graph& graph){ int item; int vecNum = 0; list<int>* adj = graph.GetAdj(); while(!zeroInDegreesQueue.empty()){ resultVec.push_back(item = zeroInDegreesQueue.front()); zeroInDegreesQueue.pop(); vecNum++; for (list<int>::iterator it=adj[item].begin(); it != adj[item].end(); ++it){ if(--inDegrees[*it] == 0) zeroInDegreesQueue.push(*it); } } if(vecNum < graph.GetVecNum()) cout<<"There is a cycle!\n"; } void printResult(int i){ cout << ' ' << i; } int _tmain(int argc, _TCHAR* argv[]) { Graph g(V); g.AddEdge(5, 2); g.AddEdge(5, 0); g.AddEdge(4, 0); g.AddEdge(4, 1); g.AddEdge(2, 3); g.AddEdge(3, 1); cout << "Following is a KahnTopological Sort of the given graph \n"; InitZeroIndegreeQueue(g); KahnTopologicalSort(g); for_each(resultVec.begin(),resultVec.end(),printResult); return 0; }
DFS:
深度优先方法,其实可看做逆向的方法,找到没有出度的点,通过递归逆推。
L ← Empty list that will contain the sorted nodes
S ← Set of all nodes with no outgoing edges
for each node n in S do
visit(n)
function visit(node n)
if n has not been visited yet then
mark n as visited
for each node m with an edge from m to n do
visit(m)
add n to L
// A recursive function used by topologicalSort void Graph::topologicalSortUtil(int v, bool visited[], stack<int> &Stack) { // Mark the current node as visited. visited[v] = true; // Recur for all the vertices adjacent to this vertex list<int>::iterator i; for (i = adj[v].begin(); i != adj[v].end(); ++i) if (!visited[*i]) topologicalSortUtil(*i, visited, Stack); // Push current vertex to stack which stores result Stack.push(v); } // The function to do Topological Sort. It uses recursive topologicalSortUtil() void Graph::topologicalSort() { stack<int> Stack; // Mark all the vertices as not visited bool *visited = new bool[V]; for (int i = 0; i < V; i++) visited[i] = false; // Call the recursive helper function to store Topological Sort // starting from all vertices one by one for (int i = 0; i < V; i++) if (visited[i] == false) topologicalSortUtil(i, visited, Stack); // Print contents of stack while (Stack.empty() == false) { cout << Stack.top() << " "; Stack.pop(); } } // Driver program to test above functions int main() { // Create a graph given in the above diagram Graph g(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); cout << "Following is a Topological Sort of the given graph \n"; g.topologicalSort(); return 0; }
参考:
https://zh.wikipedia.org/wiki/%E6%8B%93%E6%92%B2%E6%8E%92%E5%BA%8F
http://www.ivy-end.com/archives/992
相关文章推荐
- 快学Scala习题解答第十三章---------集合
- 最大公约数算法 辗转相除法解析
- IT上的本地化理解
- wince开发_摩托罗拉MC3100_打开条码设置
- 第一章
- C 语言标准输入输出函数的 函数定义 (stdio.h)
- IOS数组
- poj1011(dfs+目前做的最好的剪枝)
- HTML5新标签含义,用法及其与HTML4的区别
- WPF(VB.NET) ImageSource 转 System.Drawing.Image
- stanford parser 使用说明
- HDU -- 3605 Escape(最大流 状态合并判满流)
- 数组排序问题的两种方法:插入排序算法和递归(分治)算法
- html基本常识
- html、htm、shtml、shtm的区别
- PAT乙级 1004. 成绩排名
- mfc 子对话框数据传给父对话框
- 甲骨文官方文档,keytool使用方法, 要学习的看这个很全很权威;主要命令实录;
- 三目表达式
- java面向对象的思想