图论 最短路径--Floyd、Dijkstra、Bellman算法详解

一、Floyd算法(弗洛伊德算法)

具体的图片可参考链接https://bbs.codeaha.com/thread-4554-1-1.html 

思路:最短路flod算法,其实就是输入点的个数以及线路的个数,然后给每个点赋初值,自己的点为0,其余的点为无穷大,然后输入各条边的值,核心算法是把各个点用各个边到目标点的可能性进行对比,以求得最小值,最终以两循环输出最终最短路径。

具体步骤分布:

1.输入点的个数和边的个数(这里是输入n和m)

2.用一个两循环(由于用了一个二维数组表示两点之间边的权值e[i][j])把到自己的权值设为0,其余的都设为无穷大

3.输入各边的权值,也就是两点之间的值

4.核心算法:用三个循环比较出最短路径

如果只经过1点进行比较(两循环)e[ i ][1]+e[1][j]表示的是从i号顶点先到1号顶点,再从1号顶点到j号顶点的路程之和。还有2点、3点等等,所以每个点都进行比较就形成了三个循环。

5.用两循环输出路径

具体代码及解析:

#include <stdio.h>
int main()
{
    int e[10][10],k,i,j,n,m,t1,t2,t3;
    int inf=99999999; //这里表示两点之间没有边,也就是记为无穷大 
    
    scanf("%d %d",&n,&m);//输入点的个数n,边的个数m 
    
    //初始化
    for(i=1;i<=n;i++)
    {
    	 for(j=1;j<=n;j++)
    	 {
    	 	 if(i==j) e[i][j]=0;  //e[i][j]表示i点到j点的边的权值 
              else e[i][j]=inf;//如果是i==j就是自己到自己那就是0
			  //否则就暂时记为无穷大 
		 } 
	 } 
               
    for(i=1;i<=m;i++)
    {
        scanf("%d %d %d",&t1,&t2,&t3);
        e[t1][t2]=t3;//这里就是输入各个边的权值 
    }
    
   //核心语句
    for(k=1;k<=n;k++)//以各个点为中介进行比较 
    {
    	for(i=1;i<=n;i++)
    	{
    		 for(j=1;j<=n;j++)
    		 {
    		 	if(e[i][j]>e[i][k]+e[k][j] ) 
                e[i][j]=e[i][k]+e[k][j];//如果以一个点为中心的两边之和小于原边那么就替换 
			 }        
		}         
	} 
                
    
    
    for(i=1;i<=n;i++)
    {
         for(j=1;j<=n;j++)
        {
         printf("%10d",e[i][j]);//输出最终的结果
        }
        printf("\n");
    }
    
    return 0;
}

/*测试用例 
4 8
1 2 2
1 3 6
1 4 4
2 3 3
3 1 7
3 4 1
4 1 5
4 3 12
*/ 

 二、Dijkstra算法(迪杰斯特拉算法)

我认为Dijkstra算法与floyd算法的不同就是Dijkstra执行单源点路径,floyd执行多源点比较

思路:Dijkstra算法,其实是也是先用两循环定两个点之间的初值,然后输入权值,用一个数组dis[]记录1到各点之间的值,用book[]记录起点的值为1(这里的book的作用是为了排除权值是无穷大和0的点),核心算法是用n-1次循环先确定起点到各点的最小值,并且标记此点,然后以此点为基础,寻找下一点,然后是比较,代码和folyd代码有很大的相似处。

具体步骤:

1.输入点的个数n和边的个数m

2.用两循环定义自己到自己的值为0,其余为无穷大

3.输入各边的权值

4.用一个一维数组dis[]记录1到各点的值

5.book[]数组记录

6.核心算法:用n-1次循环找出里原点最近距离的点,然后再以此点为基础寻找下一点,以此类推

7.用一循环输出各dis[]的值

具体代码:

#include<stdio.h>
int main()
{
	int e[10][10],dis[10],book[10],i,j,m,n,t1,t2,t3,inf=99999999,u,v,min;
	scanf("%d %d",&n,&m);
	
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
		{
			if(i==j)
			e[i][j]=0;
			else
			e[i][j]=inf;
		}
	}//与floyd算法相同自己到自己是0,其余暂时赋值为无穷大 
	
	for(i=1;i<=m;i++)
	{
		scanf("%d %d %d",&t1,&t2,&t3);
		e[t1][t2]=t3;
	}//输入各边的值 
	
	for(i=1;i<=n;i++)
	{
		dis[i]=e[1][i];
	}//输入1到各边的距离 
	
	for(i=1;i<=n;i++)
	{
		book[i]=0;
		book[1]=1;
	}//将除1以外的各点记为0 
	
	//核心代码
	for(i=1;i<n;i++) 
	{
		min=inf;//先记最小值为无穷大,后面替换 
		for(j=1;j<=n;j++)
		{
			if(book[j]==0 && dis[j]<min)
			{
				min=dis[j];//寻找离1最近的点,并替换成最小值 
				u=j;//将离1最近的点记下来 
			}
		}
		book[u]=1;
		for(v=1;v<=n;v++)
		{
			if(e[u][v]<inf)
			{
				if(dis[v]>dis[u]+e[u][v])
				{
					dis[v]=dis[u]+e[u][v];
					/*以u为基础找到下一点,如果小于1直接到终点就替换*/ 
				}
			}
		}
	}
	for(i=1;i<=n;i++)
	{
		printf("%5d",dis[i]);//输出最短路径 
	 } 	
	return 0;
}
/*测试用例 
6 9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
运行结果是
0 1 8 4 13 17
*/

三、Bellman-ford算法(贝尔曼-福特算法)

个人认为Bellman算法和dijkstra算法很相似,不同的是bellman算法可以有负值,并且在算法中不用寻找离原点最近的点进行计算,少了这一步,其他的都相似,由于少了这一步,所以复杂度上升。

由于Bellman与前两个算法有很大相似性,话不多说,直接上代码:

#include<stdio.h>
int main()
{
	int dis[10],n,m,u[10],v[10],w[10],i,j;
	int inf=99999999;
	scanf("%d %d",&n,&m);//输入点数与边数 
	
	for(i=1;i<=m;i++)
	{
		scanf("%d %d %d",&u[i],&v[i],&w[i]);
	//输入两点还有其距离,这一步相当于Dijkstra的e[t1][t2]=t3; 
	}
	
	for(i=1;i<=n;i++)
	{
		dis[i]=inf;
		dis[1]=0;
	}//除自己以外都为无穷大 
		
	
	for(i=1;i<n;i++)
	{
		for(j=1;j<=m;j++)
		{
			if(dis[v[j]]>dis[u[j]]+w[j])
			{
				dis[v[j]]=dis[u[j]]+w[j];
			}
		}
	}//用n-1次循环 
	
	for(i=1;i<=n;i++)
	{
		printf("%d ",dis[i]);
	}
	return 0;
 } 
 
 /*测试用例
  5 5
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
输出
0 -3 -1 2 4
*/ 

猜你喜欢

转载自blog.csdn.net/S123KO/article/details/88594598
今日推荐