拓补排序相关定义
-
偏序
指某个集合中仅有部分成员
之间可比较。 -
全序
指集合中全体成员
之间均可比较。 -
如下图 a 表示
偏序
,b 表示全序
在图中,弧(x,y)表示 x <= y。
若在 图a 中人为的加上 v2 <= v3 ,则 图a 也是全序。 -
拓补排序
简言之,由某个集合上的一个偏序
得到该集合上的一个全序
的操作,叫做拓补排序。
全序
又叫做拓补有序
。
如何进行拓补排序
-
1、在有向图中选一个没有前驱的顶点且输出之。
2、从图中删除该顶点和所有以它为尾的弧
。
3、重复上述两步,直至 全部顶点均已输出,或者当前图中不存在无前驱的顶点为止。后一种情况说明此有向图中有环。 -
拓补排序示例
得到上 图a 的拓补有序序列为:
v6->
v1->
v4->
v3->
v2->
v5 -
当有向图无环时,也可利用
深度优先遍历
进行拓补排序,因为图中无环,则由图中某点出发进行深度优先搜索遍历时,最先退出 DFS 函数的顶点
即出度为零的顶点
,是拓补有序序列
中最后一个顶点。
由此,按退出 DFS 函数的先后
记录下来的顶点序列(如同求强连通分量时,finished 数组中的顶点序列)即为逆向的拓补有序数列。
代码实现拓补排序
- 采用
邻接表
作有向图的存储结构,且在头结点中增加一个存放顶点入度的数组
。
入度为 0 的顶点
即为没有前驱的顶点
;
删除顶点及以它为尾的弧的操作,可换以弧头顶点的入度减 1 来实现。
为了避免重复检测入度为 0 的顶点,可另设一栈暂存所有入度为 0 的顶点。
#define MAX_VERTEX_NUM 20
typedef struct ArcNode
{
int adjvex; // 该弧所指向的顶点的位置
struct ArcNode *nextarc; // 指向的下一条弧的指针
InfoType *info; // 指向该弧相关信息的指针
} ArcNode; // 表中结点,表示一条弧(也可以是一条边)
typedef struct VNode
{
VertexType data; //顶点信息
ArcNode *firstarc; //指向第一条依附于该顶点的弧的指针
} VNode, AdjList[MAX_VERTEX_NUM];
typedef struct
{
AdjList vertices; //存放顶点结点的顺序表
int vexnum, arcnum; //图的当前顶点数和弧数
int kind; //图的种类,标志
} ALGraph;
Status TopologicalSort(ALGraph G)
{
// 对图中各个顶点求入度 indegree[0 .. vernum-1]
FindInDegree(G, indegree);
InitStack(S);
// 建零入度顶点栈 S
for(i=0; i<G.vexnum; i++)
{
if(!indegree[i])
{
Push(S, i);
}
}
// count 用于对输出顶点计数
count = 0;
while(!StackEmpty(S))
{
Pop(S, i);
// 输出 i 号顶点 并 计数
printf(i, G.vertices[i].data);
count++;
// 对 i 号顶点的每个邻接点的入度减 1
for(p=G.vertices[i].firstarc;
p;
p=p->nextarc)
{
k = p->adjvex;
// 若新产生入度为 0 的顶点
// 当场入栈
if(!(--indegree[k]))
{
Push(S, k);
}
}
}
// 该图存在回路
if(count<G.vexnum)
{
return ERROR;
}
else
{
return OK;
}
}
- 时间复杂度
对 n 个顶点 和 e 条弧的有向图而言,求各个顶点的入度的时间复杂度为 O( e );
建 0 入度顶点栈 的时间复杂度为 O( n );
在拓补排序过程中,若有向图无环,每个顶点进一次栈,出一次栈,入度减1 的操作在 while 语句中共执行 e 次,
所以总的时间复杂度
为 O( n+e )