7.5.1 拓补排序(有向图)

拓补排序相关定义

  • 偏序
    指某个集合中仅有 部分成员 之间可比较

  • 全序
    指集合中 全体成员 之间均可比较。

  • 如下图 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 )
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值