定义:
图的拓扑排序是对有向无环图来说的,无向图和有环图没有拓扑排序,或者说不存在拓扑排序,对于一个有向无环图G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若图G存在边< u, v >,则u在线性序列中出现在v之前。对一个有向无环图进行拓扑排序产生的线性序列称为满足拓扑次序的序列。一个有向无环图可以表示某种动作或者方案,或者状态,而有向无环图的拓扑序列通常表示某种某案切实可行或者各个成员之间的某种关系.
举个栗子,看上面那个图,v1v2v3v4v5v6v7v8是有向图的一个拓扑序列,同时v1v3v5v2v4v6v7v8也是有向图的一个拓扑序列,即对于一个都有向图来说拓扑序列不止一个,在拓扑序列中所有有向边的起点都在终点的左边,也就是如若有向图G中从顶点vi到vj有一条路径,则在拓扑序列中顶点vi必须在顶点vj之前.
算法:
(1):在有向图中选一个没有前驱(入度为0)的顶点输出之。
(2):从图中删除该顶点和所有以它为尾的弧(从该点出发的所有有向边)。
(3):重复上述步骤,直至全部顶点均已输出,或者当前图中不存在没有前驱的顶点为止.后一种情况则说明图中有环。
实现算法事采用链式前向星存储图,再开一个额外的数组存储每个点的入度,每删除一个点,就遍历以这个点为起点的边,将边对应的终点的入度减1,即可选择并删除下一点。在如何选择下一个点的问题上,需要开一个栈,当出现入度为0的顶点是就把其压入栈,然后每次从栈顶获得一个元素,一定是下一个待扩展元素。
#include <iostream> #include <cmath> #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <stack> using namespace std; typedef long long LL; const int maxN = 100 + 3; const int maxM = 1000 + 3; int head[maxN]; int N, M; struct EdgeNode { int to; int w; int next; }; EdgeNode Edges[maxM]; int indegree[maxN];//用于记录每个点的入度 //获得每个点的入度 int getIdg() { memset(indegree, 0, sizeof(indegree)); for(int i = 1; i <= M; i++) indegree[ Edges[i].to ]++; } void tplgSort() { getIdg(); stack<int> st; for(int i = 1; i <= N; i++) //把入度为 0 的顶点压入栈中 if( !indegree[i] ) st.push(i); while( !st.empty() ) { int tp = st.top(); //每次从栈顶获得一个顶点,一定是下一个待扩展顶点 cout << tp << " "; st.pop();//弹出栈顶元素 //删除从该节点出发的全部有向边,更新 indegree 数组 for(int i = head[ tp ]; i != -1; i = Edges[i].next ) { --indegree[ Edges[i].to ];//对于从 i 号节点的每个邻接节点入度 -1 if( !indegree[ Edges[i].to ] ) //如果入度为 0 则入栈 st.push( Edges[i].to ); } } } int main() { //freopen("input.txt", "r", stdin); memset(head, -1, sizeof(head)); cin >> N >> M; for(int i = 1; i <= M; i++) { int x, y; cin >> x >> y; Edges[i].to = y; //Edges[i].w = z; Edges[i].next = head[x]; //与边i同起点的下一节点位置 head[x] = i; } tplgSort(); return 0; }