图--有向无负权回路的单源最短路径 DAG 算法导论p365

本文介绍了一种针对有向无环图(DAG)的最短路径算法,该算法能够高效地找出图中从源点到其他各点的最短路径。通过拓扑排序和仅对各边进行一次松弛操作,算法确保了计算复杂度为O(V+E)。

DAG_SHORTEST_PATHS该算法是对某加权的有向无回路图(即DAG图),特指的是无负权回路图,即可是带负权边的图或是带正值的回路图。时间是(V+E)

实现步骤是:

1、建立有向邻接表

2、然后DAG_SHORTEST_PATHS函数:该函数主要是对图dag

            1、进行拓扑排序(即是1、对图进行深搜  2、按深搜完成时间有大到小排列 (这是利用point向量对 图进行排序,所以各点在AdjList向量中的位置不变)

            2、然后按拓扑排序的顺序只对各边松弛一次就即可

3、输出

                    

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 99999999//表示无穷大
typedef struct node
{
	int adjvex;//连接点
	int weight;//边的权值
	struct node *next;
}EdgeNode;

typedef struct vnode
{
	char str[20];
	int pre;//该点深搜时的前驱
	int dis;//节点发现时间
	int fish;//记录该顶点完成时间即该点标为黑色的时间
	int color;//1代表白色表示该点尚未发现 2代表灰色现已发现 3代表黑色表示与它邻接的边已发现
	int distance;//源点到该的距离
	EdgeNode *firstedge;
}VertexNode;
VertexNode AdjList[10001];

typedef struct 
{
	int fish;//完成时间
	int id;// 指示拓扑排序后  AdjList向量中分量的位置
}dpoint;
dpoint point[10001];
int time;//时间计数器

void CREAT_ADJLIST_GRAPH(int n,int e)  //节点及边的录入 以及建立邻接表
{  
	int i,j,s,d,find,weight;
    char str1[20],str2[20];
	EdgeNode *p;
	printf("请输入一次输入所有字符节点 且第一个输入的点默认为源节点\n");
	for(i=0;i<n;i++)
	{
		scanf("%s ",&AdjList[i].str);
		AdjList[i].firstedge=NULL;
	}
	printf("请输入各条边\n");
	for(i=1;i<=e;i++)//建立邻接表 由于是无向图则要申请两个节点p和q
	{   
		find=0;
		scanf("%s %s %d",&str1,&str2,&weight);// u ->v &权值
		for(j=0;find!=2&&j<n;j++) 
			if(strcmp(str1,AdjList[j].str)==0)
			{
				s=j;
				find++;         
			}
			else if(strcmp(str2,AdjList[j].str)==0)
			{
				d=j;
				find++;   
			}
			p=(EdgeNode*)malloc(sizeof(EdgeNode));  
			p->adjvex=d;
			p->weight=weight;
			p->next=AdjList[s].firstedge;  
			AdjList[s].firstedge=p;
	}
}

void DFS_VISIT(int u)//深度搜索递归函数
{  
	int v;
	EdgeNode *p;
	AdjList[u].color=2;//u节点已发现 标记为灰色
	time++;
	AdjList[u].dis=time;
	p=AdjList[u].firstedge;
	while(p)
	{
		v=p->adjvex;
		if(AdjList[v].color==1)
		{
			AdjList[v].pre=u; //记录v的前驱u
			DFS_VISIT(v);//递归深搜
		}
		p=p->next;
	}  
	AdjList[u].color=3;//u节点所有邻接点均已发现,即完成任务,标记为黑色
	AdjList[u].fish=time=time+1;//标记完成时间 
}

void DFS(int n)//深度搜索
{    
	int i;
	for(i=0;i<n;i++)
	{
		AdjList[i].color=1;//1代表白色表示该点尚未发现
		AdjList[i].pre=-1;//前驱向量初始化
	}	
	time=0;
	for(i=0;i<n;i++)
		if(AdjList[i].color==1)
			DFS_VISIT(i);   
}

int cmp(const void *a,const void *b)
{
	return(  ((dpoint*)b)->fish-((dpoint*)a)->fish  );//从大到小排列
}

void TOPOLOGICAL_SORT(int n)//拓扑排序(其实是对point向量来拓扑排序)
{
	int i;
	DFS(n);//对顶点进行深度搜索
    for(i=0;i<n;i++)
	{
		point[i].fish=AdjList[i].fish;
		point[i].id=i;
	}
	qsort(point,n,sizeof(point[0]),cmp);//时间按完成时间由大到小排序
}

void printpath(int i)//路径输出   
{  
    if(AdjList[i].pre==-1)  
    {  
        printf("%s->",AdjList[i].str);  
        return ;  
    }  
    else  
    {  
            printpath(AdjList[i].pre);  
            printf("-%s",AdjList[i].str);  
    }  
} 

void print(int n)//输出   
{  
    int i;   
	//for(i=0;i<n;i++)//排序输出
	//	printf("%7s %d",AdjList[i].str,AdjList[i].distance);
//	printf("\n");
	for(i=0;i<n;i++)  
		if(AdjList[i].pre!=-1)  
		{  
			printf("%d: ",AdjList[i].distance);  
			printpath(i);  
			printf("\n");                 
		}  
		else if(i==0)		
			printf("%d: %s\n",AdjList[i].distance,AdjList[i].str);						
		else  
			printf(" 无穷大 %s\n",AdjList[i].str);  
		printf("\n");
		
} 

void INITIALIZE_SINGLE_SOURCE(int n)//
{
	int i;
	for(i=0;i<n;i++)
	{   
		AdjList[i].pre=-1;
		AdjList[i].distance=MAX;
	}
	AdjList[0].distance=0;//源点 的距离初始化为0;
}

void RELAX(int u,int v,int weight)//松弛技术
{
	if(AdjList[v].distance>AdjList[u].distance+weight)
	{
		AdjList[v].distance=AdjList[u].distance+weight;
		AdjList[v].pre=u;
	}
}

void DAG_SHORTEST_PATHS(int n)
{
	int u,v,weight,i;
	EdgeNode *p;
	TOPOLOGICAL_SORT(n);//拓扑排序
	INITIALIZE_SINGLE_SOURCE(n);
	for(i=0;i<n;i++)//按完成时间由大到小依次对每条边进行松弛
	{
		u=point[i].id;//u 指示(的是拓扑中完成时间较晚的量)在AdjList向量中的位置
		p=AdjList[u].firstedge;
	    while(p)
		{
			v=p->adjvex;
			weight=p->weight;
			RELAX(u,v,weight);//进行松弛
			p=p->next;
		}
	}
	print(n);//输出
}

int main()
{   
	freopen("1.txt","r",stdin);
	int n,e;
	printf("请输入字符节点的个数N 和边的数目E:\n");
	scanf("%d%d ",&n,&e);
	CREAT_ADJLIST_GRAPH(n,e); //节点及边的录入 以及建立邻接表
	DAG_SHORTEST_PATHS(n);  //求DAG图单源最短路径
	return 0;
}


 

    

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gdliweibing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值