图的拓扑排序算法

什么是图的拓扑排序?

在实际应用中,有向图的边可以看做是顶点之间制约关系的描述。把顶点看作是一个个任务,则对于有向边<Vi,Vj>表明任务Vj的启动需等到任务Vi完成之后,也就是说任务Vi先于任务Vj完成。

对于一个有向图,找出一个顶点序列,且序列满足:若顶点Vi和Vj之间有一条边<Vi,Vj>,则在此序列中顶点Vi必在顶点Vj之前。这样的一个序列就称为有向图的拓扑序列(topological order)。

总之就是:如果G=(V, E)是一个无环的有向图,则G上的拓扑排序指的是图中顶点满足下列条件的一种排序。若 <v, w> ∈E,则在顶点v必须位于顶点w之前。

为了方便起见,下面将针对邻接矩阵存储。

对于拓扑排序算法,采用如下的算法实现:

  1. 从有向图中选取一个没有前驱(入度为0)的顶点输出。
  2. 删除图中所有以它为起点的弧。

重复上述两个步骤,此时会出现两种情形

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

相关推荐