图的拓扑排序算法
什么是图的拓扑排序?
在实际应用中,有向图的边可以看做是顶点之间制约关系的描述。把顶点看作是一个个任务,则对于有向边<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=;
for(int i=;i<max_size;i++)
if(adjacent[i][vertex] == )
num++;
return num;
}
//拓扑排序算法
void Graph::topologicalsort()
{
int vertex;
queue<int> q; //用对列来存放已经入度为0的结点
for(int i=;i<max_size;i++)
if(indegree[i] == )
q.push(i);
bool visited[max_size]; //存放访问过的结点
for(int i=;i<max_size;i++)
visited[i] = false;
while(!q.empty()) //当队列非空的时候循环
{
//每次取出一个队列头部的元素进行访问并且输出
vertex = q.front();
q.pop();
cout<<vertex;
visited[vertex] = true;
//遍历找到与此结点相邻的断开从此结点出发的弧之后的入度为0的结点,并且压入队列里面
for(int i=;i<max_size;i++)
if(adjacent[vertex][i] == )
{
if((--indegree[i]) == )
q.push(i);
}
}
//检测入度为0的点都放入queue里面后还有没有结点没有访问到
for(int i=;i<max_size;i++)
if(visited[i] == false)
cout<<"这是个有环的有向图..."<<endl;
delete []visited;
}参考来源:http://blog.csdn.net/zhangxiangdavaid/article/details/38353517