拓补排序相关定义
-
偏序
指某个集合中仅有部分成员之间可比较。 -
全序
指集合中全体成员之间均可比较。 -
如下图 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 )
本文深入探讨了图论中的偏序与全序概念,并详细解释了拓补排序的定义和作用。拓补排序是将有向无环图(DAG)的偏序关系转化为全序序列的过程。介绍了拓补排序的两种实现方式:通过寻找无前驱顶点输出以及深度优先遍历。同时,提供了基于邻接表的数据结构和入度的代码实现,阐述了拓补排序的时间复杂度。拓补排序在解决依赖关系排序问题中具有广泛应用。
743

被折叠的 条评论
为什么被折叠?



