拓扑排序算法

来源:互联网 发布:java简单金字塔代码 编辑:程序博客网 时间:2024/06/15 07:04

拓扑排序算法

@(算法学习)

AOV网

说到拓扑排序首先需要看的一个概念是Activity On Vertex 网。关于AOE前面做过具体的探究。

http://blog.csdn.net/u011240016/article/details/53171808?locationNum=1&fps=1

关于如何寻找最大路径文中有具体的思考。

而既然活动可以在边上,就必然意味着,活动可以在结点上。

这样做有什么含义吗?

有。在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图就可以用于研究哪些活动必须先完成了。

比如常见的选课,有些课是需要先导课程的,如果没有学过,就没法选。课程量少时,当然可以一眼看穿,如果全校范围内,课程数很多,就需要借助计算机来帮忙了。

AOV中不允许出现回路。这个很容易理解,即,你选的课程先导课程不能是更难的课吧。只有学完了简单的先导课才能去学难一点的课程。能直接学更难的,再反过来修先导课吗?如果硬是说可以,我也不说啥了。。总而言之,我们认定了,AOV不许有回路。反之,拓扑排序也可以用于检测图中是否有环

拓扑排序的思想

设G=(V,E),是一个具有n个顶点的有向图,V中的顶点序列V1,V2,...,Vn称为一个拓扑序列。当且仅当满足以下条件:

  • 从顶点ViVj有一条路径,则在顶点序列中顶点Vi必须在顶点Vj之前。

这种讲法是为了更加容易编码,实际自己理解时,可以这样想,找到一个没有箭头指向自己的结点,去掉它以及与它相连的边;再找这样的没有箭头指向自己的结点,依次继续。如果最终所有的结点都能被去掉,则这个AOV可以用拓扑排序构造拓扑序列。这个有向图就没有环。

此外,拓扑排序不一定就是一个。

输出拓扑序列的量化为步骤就是:

  • 从AOV网中选择一个没有前驱的顶点并输出;
  • 从AOV网中删除该结点,并删除所有以该结点出发的的弧。有时候说是弧尾,在有向图中,弧尾是出发点,弧头是结尾。这是根据箭头所在的地方称之为头得来的。

结果分析:

  • AOV中全部顶点被输出,则AOV网中不存在回路。
  • AOV网中顶点未被全部输出,则AOV网中存在回路。

拓扑排序算法基于的数据结构

一般来说,拓扑排序中的图的存储结构用的是邻接表

为什么?

因为拓扑排序时需要查找以某个顶点出发的弧,此外,还需要查找入度为0的结点。即,找没有箭头指向自己的结点。

为此,在顶点表中加入一个入度域即可满足全部要求。

如何查找没有前驱的顶点呢?
查找入度为0的顶点。这还不够优化,即,为了加快查找,可以用栈来存放。把度为0的顶点存放在栈中。

先写伪代码,具体实现很简单:

  • 初始化栈S,计数变量cnt;
  • 扫描顶点表,将入度为0的顶点压栈
  • 当栈非空时循环:
    • 栈顶结点Vi出栈,输出Vj,并且cnt++;
    • 将顶点Vj的各个邻接点入度减1;
    • 将新的入度为0的顶点入栈

具体代码:

void TopSort(){    int top = -1, cnt = 0; // 顺序栈S并初始化,计数初始化,top指向栈顶元素    for(int i = 0; i <vertexNum; i++) // 扫描顶点表,找入度为0的点    {        if(adjList[i].in == 0)        {            S[++top] = i; // 先更新top指针        }         while(top != -1) // 栈非空        {            int j = S[top--]; // 取出栈顶并更新top            cout << adjList[j].vertex;            cnt++;            p = adjList[j].firstEdge;            while(p != NULL)// 找出顶点j的所有出边            {                k = p->adjvex;                adjList[k].in--; //所有与j相邻的结点入度减1                if(adjList[k].in == 0)                {                    S[++top] = k; //加入栈                }                p = p->next; // 邻接表下的next指向的是另一个兄弟            }        }    }    if(cnt < vertexNum) cout<< "有回路" << endl;}

所以可见时间复杂度是O(n+e)ne

0 0