拓扑排序(概念+算法+代码,基于AOV网络)

1.拓扑排序是对谁排序

先明确一个概念,即AOV网络,这个网络简单来讲就是一张有向图,表示一个流程,图的顶点表示活动(或者说完成一个流程的步骤),而有向边表示活动的优先关系,比如A——>B,表示先完成A,才能完成B。

拓扑排序的排序对象就是这些顶点,也就是活动,按照拓扑排序得到的序列去执行每一个活动,不会出现步骤错乱的情况(比如A——>B,必须先完成A再完成B,反过来就不可以)。

当然,拓扑排序也可用于排序AOE网络,本文主要讲AOV网络。

2.拓扑排序的算法思想

核心是:重复选择没有直接前驱的顶点。

具体来讲,步骤如下:

1. 构建一个AOV网络,令 n 为顶点个数;

2. 在AOV网络中选一个没有直接前驱的顶点并输出;

3. 从图中删去该顶点, 同时删去所有它发出的有向边;

4. 重复以上 2、3 步,跳出循环的可能性有两个:

第一种可能,所有顶点均被输出,这时候得到的输出序列就是拓扑排序的过程;

第二种可能,图中还有未输出的顶点,但已经找不到“没有直接前驱的顶点”了(也就是说,图中存在环,每个顶点都有指向它的边),这时候也要退出循环。

注意:.一个AOV网的拓扑排序不是唯一的,但如果用具体的存储结构来表示AOV网络,那么序列就是唯一的。换言之,给一张图让你写出拓扑排序序列,也许有多种写法,但如果还扔给你一张表示AOV网络的邻接矩阵,那序列就是唯一的了。

3.算法实现的几个细节

1.如何说明一个顶点“没有直接前驱”(入度为0)?

设置一个大小为n(顶点个数)的数组——Indegree[n],用来存储每个节点的入度(有几条边指向)。

2.打印完一个节点之后,如何删除从它发出的有向边?

假设图的存储结构是邻接表,打印完一个节点后,顺着链表依次遍历它的邻接点,每一个邻接点对应的入度都减1,如果减完后为0,就说明这是一个新产生的入度为0的点,需要把该节点放到一个栈里,便于以后处理它(这里不一定要用栈,用队列也可,只要是个能存能取的容器就好,目的是暂时存一下,方便以后拿出来处理)。

4.代码实现

#include<iostream>
#include<stack>
#define MAX_VEX_NUM 100
using namespace std;


//邻接点结构体
struct AdjVex {
	int val;//点的信息
	int index;//点的索引
	AdjVex* nextAdjVex;//相当于链表的next指针
};
//顶点结构体
struct Vertex {
	int val;//顶点信息
	AdjVex* firstAdjVex;//某个点的第一个邻接点,所谓的第一指的是在链表中最靠前的
};
//AOV网络结构体
struct ALGraph {
	int vexnum;//顶点数
	Vertex arr[MAX_VEX_NUM];//顶点结构体数组
};

int Indegree[MAX_VEX_NUM];

int TopologicalSort(ALGraph G)
{
	//假设Indegree数组已经被初始化好,每个顶点都存着正确的入度
	stack<int> stk;//栈存储定点在arr数组中的索引
	for (int i = 0; i < G.vexnum; i++) {// 将所有入度为0的节点索引入栈
		if (!Indegree[i])
			stk.push(i);
	}
	int count = 0;//用来记录输出了几个顶点
	while (!stk.empty()) {//栈不空则继续循环
		//从栈中取一个顶点(其实是取顶点索引)
		int i = stk.top();
		stk.pop();
		//输出顶点的编号并计数(索引不是编号)
		cout << G.arr[i].val << ' ';
		count++;
		//删除出栈顶点发出的有向边
		for (AdjVex* p = G.arr[i].firstAdjVex; p; p = p->nextAdjVex) {
			int k = p->index;//当前邻接点的索引
			//如果入度不为0,就要减1
			if (Indegree[k]!= 0) {
				Indegree[k]--;
			}
			//减完1后判断入度是否为0,若为0则索引入栈
			if (!Indegree[k]) {
				stk.push(k);
			}
		}
		//判断有无回路
		if (count < G.vexnum)return 0;
		else return 1;
	}
}

欢迎各位道友纠错指正!

祝你拨云见日,解决困惑!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值