C语言-AOV网与拓扑排序

  1. 邻接表:
  • 顶点下标查找函数(LocateVex)
  • 创建有向图的邻接表(CreateDG)
  • 邻接表打印函数(print)
  1. 拓扑排序(TopologicalSort)

AOV网与AOE网:

  1. AOV网(Activity On Vertex NetWork):用顶点表示活动,边表示活动发生的 先后关系

  2. AOE网(Activity On Edge NetWork):在带权有向图中若以顶点表示事件,有向边表示活动,边上的权值表示该活动持续的时间,AOE网通常用于 估算事件/工程完成的工期(时间)

  3. 关键路径:是从开始点到完成点的 最长路径的长度。路径的长度是边上活动耗费的时间。

  4. AOV网络与AOE网络的关系

    从定义上来看,很容易看出两种网的不同,AOV网的活动以顶点表示,而AOE网的活动以有向边来表示,AOV网的有向边仅仅表示活动的先后次序。纵观这两种网图,其实它们总体网络结构是一样的,仅仅是活动所表示的方式不同,因此可以猜想从AOV网转换成AOE网应该是可行的。

    通常AOE网都是和关键路径联系在一起的,在AOE网中我们可以通过关键路径法来计算影响整个工期的关键路径,达到缩短工期的目的。在传统的AOV网中是没有表示活动时间的权值的,因此传统的AOV网无法估算工期,但是如果我们在AOV网中的活动结点上都标上时间属性,那么AOV网就可以完全转换为AOE网。

什么是拓扑排序?

  1. 拓扑序列:在AOV网(无环图)中,由顶点Vi到顶点Vj有一条路径,则在该线性序列中的顶点Vi必定在顶点Vj之前。
  2. 拓扑排序:将AOV网中的顶点序列排成拓扑序列就叫拓扑排序
  3. 拓扑排序条件:必须是有向无环图(Directed Acycline Graph),AOV网满足有向无环图条件,若是有向成环图,则会进入死循环。

拓扑排序算法思路

  • 拓扑排序的执行过程相当于每次 删去入度为0的顶点这个顶点发射出去的边,那么我们每次删去一个顶点和其发射边,就会生成一个新图,在这个新图上继续执行删去入度为0的顶点这个顶点发射出去的边,直到所有顶点都被删完。删除每个顶点的顺序,就是拓扑序列。
    C语言-AOV网与拓扑排序_第1张图片

拓扑排序如何实现?

  • 对一个顶点而言:拓扑排序主要是对入度为0的顶点做处理,处理的内容包括:1.顶点本身;2.顶点发出的边,去掉顶点的同时要删去这些有向边,也就是要更新当前顶点的邻接顶点的入度。
  • 对所有顶点而言:重复执行上述操作,直到把所有入度为0的顶点处理完毕,即拓扑排序完成。
  1. 我们首先找到入度为0的顶点,然后对其进行输出,接着根据邻接表的邻接关系,找到与其邻接的其他顶点,再去对其他顶点进行相同的处理,由此往复。
  2. 我们需要一个 临时存取空间space,我们每次把入度为0的顶点放入space中,然后按顺序(顺序可以从头开始、从尾开始、甚至可以任意取)从space中取出,然后进行步骤1的处理,再将更新后入度为0的顶点放入space中,直到space中的元素被取空,即拓扑排序结束。
  3. 临时存取空间space的存取顺序可以是任意的,所以,这个space可以是栈结构,队列结构,也可以是一个数组,甚至可以是其他(源代码用数组模拟栈结构)。由此可见,存取顺序的不同,直接导致了拓扑排序结果不唯一。
  4. 总结一下拓扑排序在程序中的执行流程:首先我们搞了一个临时存储空间space,然后将入度为0的顶点放入space,再按顺序取出,每去取出一次,就根据邻接表的邻接关系,查找当前取出的顶点的邻接点,对每个邻接点入度-1,更新完入度后,看看有没有出现新的入度为0的结点,将其放入space中,直到space为空时,即所有顶点处理完毕,输出的序列就是拓扑序列。

可能产生的疑问

  • 问题1:什么原因会导致拓扑序列产生不同的结果?
  1. 临时存取空间space的结构、存取方式会直接影响对拓扑排序结果
  2. (我给忘了…)
  • 问题2:一个顶点会不会被重复放入临时存取空间space中,会不会对一个结点重复访问,是否需要像DFS/BFS那样设置visited数组记录结点访问状态?
  1. 在拓扑排序算法中,没有顶点会被多次访问,也不需要visited数组对访问状态记录,每个顶点只访问一次。原因是:拓扑排序的前提是在 有向无环图 中不会存在顶点之间有多分支回路的情况,如果注入水流,水流方向是只能向前的,无法回头,因为不存在环。
  • 问题3:待补充

完整源代码:

#include 
#include 
#define VertexType char //顶点的数据类型(char) 
#define VertexMax 20 //最大顶点个数 

typedef struct ArcNode//边表 
{
	int adjvex;//存储的是该顶点在顶点数组即AdjList[]中的位置 
	struct ArcNode *next;
}ArcNode;

typedef struct VNode //顶单个点 
{
	VertexType vertex;
	struct ArcNode *firstarc;
}VNode;

typedef struct //顶点表 
{
	VNode AdjList[VertexMax];//由顶点构成的结构体数组 
	int vexnum,arcnum; //顶点数和边数 
}ALGraph;

int LocateVex(ALGraph *G,VertexType v)
{    
    int i;
	for(i=0;i<G->vexnum;i++)
	{
		if(v==G->AdjList[i].vertex)
		{
			return i;
		}
	}
	
	printf("No Such Vertex!\n");
	return -1;
}

//有向图 
void CreateDG(ALGraph *G)
{
	int i,j;
	//1.输入顶点数和边数 
	printf("输入顶点个数和边数:\n");
	printf("顶点数 n="); 
	scanf("%d",&G->vexnum);
	printf("边  数 e="); 
	scanf("%d",&G->arcnum);
	printf("\n"); 
	
	printf("\n");
	//2.顶点表数据域填值初始化顶点表指针域 
	printf("输入顶点元素(用空格隔开):");
	for(i=0;i<G->vexnum;i++)
	{
		scanf(" %c",&G->AdjList[i].vertex);
		G->AdjList[i].firstarc=NULL;
	} 
	printf("\n");
	
	//3.输入边信息构造邻接表 
	int n,m;
	VertexType v1,v2;
	ArcNode *p1,*p2; 
	
	printf("请输入边的信息:\n\n"); 
	for(i=0;i<G->arcnum;i++)
	{   //输入边信息,并确定v1和v2在G中的位置,即顶点在AdjList[]数组中的位置(下标)  
		printf("输入第%d条边信息:",i+1); 
		scanf(" %c%c",&v1,&v2);
		n=LocateVex(G,v1);
		m=LocateVex(G,v2);
		
		if(n==-1||m==-1)
		 {
		 	printf("NO This Vertex!\n");
		 	return;
		 } 
		
		p1=(ArcNode *)malloc(sizeof(ArcNode));
		p1->adjvex=m;//填上坐标 
		p1->next=G->AdjList[n].firstarc;//改链(头插法)  
		G->AdjList[n].firstarc=p1;
	
	}//for  
} 

void print(ALGraph G)
{
	int i;
	ArcNode *p;
	printf("\n-------------------------------");
	printf("\n图的邻接表表示:\n");
	
	for(i=0;i<G.vexnum;i++)
	{
		printf("\n   AdjList[%d]%4c",i,G.AdjList[i].vertex);
		p=G.AdjList[i].firstarc;
		
		while(p!=NULL)
		{
			printf("-->%d",p->adjvex);
			p=p->next;
		}
	 } 
	 printf("\n");
} 

void TopologicalSort(ALGraph *G)
{//文中说的space就是此处的栈结构
 //此处的栈结构我用的是数组来模拟的(因为我懒)
	int i;
	int top=-1;//栈顶指针 
	int Gettop;//用于存储/获取栈的栈顶元素 
	int count=0;//用于统计拓扑排序生成的结点数(若生成结点数 < 图的结点数,则代表图中有环,拓扑排序不成功) 
	int stack[VertexMax]={0};//栈 
	int indegree[VertexMax]={0};//入度数组 
	struct ArcNode *p;//临时变量 
	
	//1.计算顶点入度,并存入indegree数组中
    for(i=0;i<G->vexnum;i++)
     {
     	if(G->AdjList[i].firstarc!=NULL)
     	{
     		p=G->AdjList[i].firstarc;
     		while(p!=NULL)
     		{
     			indegree[p->adjvex]++;
     			p=p->next;
			}
		}
	 }
     
    //2.初始化部分:将初始入度为0的顶点入栈
	for(i=0;i<G->vexnum;i++)
	{
		if(indegree[i]==0)
		{
			stack[++top]=i;//先将指针加一在进行存储 
		}	
	} 
    
    //3.拓扑排序
	while(top!=-1)//栈不为空 
	{
		Gettop=stack[top--];//获取栈顶元素,并且栈顶指针减一 
		printf(" %c",G->AdjList[Gettop].vertex);//输出栈顶元素 
		count++;
		 
		p=G->AdjList[Gettop].firstarc; 
		while(p!=NULL)
		{
			indegree[p->adjvex]--;
			
			if(indegree[p->adjvex]==0)
			{
				stack[++top]=p->adjvex;
			}
			p=p->next;
		}
	} 
	
	//4.判断拓扑排序是否成功(生成结点数 < 图的结点数,则代表图中有环,拓扑排序不成功) 
	if(count<G->vexnum) 
	   printf("TopologicalSort Failed!\n");
	else return;     
}

int main() 
{
	ALGraph G;
	CreateDG(&G); 
	print(G);
	printf("\n拓扑排序结果:"); 
	TopologicalSort(&G);
	
	return 0;
}

执行结果

C语言-AOV网与拓扑排序_第2张图片


参考:

  1. AOV网与AOE网部分内容参考自:AOV网络与AOE网络——lx青萍之末
  2. 参考教材:严蔚敏数据结构(C语言版||第二版)

你可能感兴趣的:(数据结构,数据结构,c语言,算法,拓扑学,图论)