图的拓扑排序算法
什么是图的拓扑排序?
在实际应用中,有向图的边可以看做是顶点之间制约关系的描述。把顶点看作是一个个任务,则对于有向边<Vi,Vj>表明任务Vj的启动需等到任务Vi完成之后,也就是说任务Vi先于任务Vj完成。
对于一个有向图,找出一个顶点序列,且序列满足:若顶点Vi和Vj之间有一条边<Vi,Vj>,则在此序列中顶点Vi必在顶点Vj之前。这样的一个序列就称为有向图的拓扑序列(topological order)。
总之就是:如果G=(V, E)是一个无环的有向图,则G上的拓扑排序指的是图中顶点满足下列条件的一种排序。若 <v, w> ∈E,则在顶点v必须位于顶点w之前。
为了方便起见,下面将针对邻接矩阵存储。
对于拓扑排序算法,采用如下的算法实现:
- 从有向图中选取一个没有前驱(入度为0)的顶点输出。
- 删除图中所有以它为起点的弧。
重复上述两个步骤,此时会出现两种情形
1.所有顶点都已输出,输出序列就是拓扑序列。
2.已没有无前驱的顶点,但任然有顶点没有输出,这表明该有向图是有环的。
可见拓扑排序可以检测有向图是否有环。
算法说明:
使用一个int型的数组indegree,来表示每个顶点的入度。于是,通过查找数组indegree就可得到前驱为零的顶点。下面的代码采取另一种做法:使用一队列,入度为零的顶点入队。这样每次输出入度为零的顶点时,只需出队即可,不必每次都检测整个indegree数组。
具体实现如下所示:
//采用邻接矩阵的形式来存放结点和边的信息 template <int max_size> class Graph { private: int count; bool adjacent[max_size][max_size]; //邻接矩阵中顶点用数组下标表示 public: int indegree(int); //取得结点入度的函数 void Graph::topologicalsort(void); //拓扑排序函数 }; //返回结点的入度的函数 int Graph::indegree(int vertex) { int num=0; for(int i=0;i<max_size;i++) if(adjacent[i][vertex] == 0) num++; return num; } //拓扑排序算法 void Graph::topologicalsort() { int vertex; queue<int> q; //用对列来存放已经入度为0的结点 for(int i=0;i<max_size;i++) if(indegree[i] == 0) q.push(i); bool visited[max_size]; //存放访问过的结点 for(int i=0;i<max_size;i++) visited[i] = false; while(!q.empty()) //当队列非空的时候循环 { //每次取出一个队列头部的元素进行访问并且输出 vertex = q.front(); q.pop(); cout<<vertex; visited[vertex] = true; //遍历找到与此结点相邻的断开从此结点出发的弧之后的入度为0的结点,并且压入队列里面 for(int i=0;i<max_size;i++) if(adjacent[vertex][i] == 1) { if((--indegree[i]) == 0) q.push(i); } } //检测入度为0的点都放入queue里面后还有没有结点没有访问到 for(int i=0;i<max_size;i++) if(visited[i] == false) cout<<"这是个有环的有向图..."<<endl; delete []visited; }
参考来源:http://blog.csdn.net/zhangxiangdavaid/article/details/38353517
少一些功利主义的追求,多一些不为什么的坚持!