最短路径常用算法总结

Dijkstra算法求最短路

适用范围:单源点最短路径,并且路径花费为飞负值。

算法描述:(可求具体路径)

说明:visited[N]为标记数组,map[N][N]为图的邻接矩阵,path[N]为路径存储数组

dijkstra(int v0,int n)
{
    int v,i,min,w,next,k;
    memset(visited,0,sizeof(visited));
    for(v=0;v<n;v++)
        d[v]=0x7FFFFFFF;//初始化为权值
    path[v0]=v0;//p[v]
    d[v0]=0;
    visited[v0]=1;
    for(i=1,v=v0;i<n;i++)
    {        
        for(w=0;w<n;w++)
        {
            if(visited[w]==0&&map[v][w]!=0x7FFFFFFF)
			{
				if(d[v]<d[w]-map[v][w])//松弛操作
				{
					d[w]=d[v]+map[v][w];
					path[w]=v;
				}
			}
        }
        min=0x7FFFFFFF;
        for(w=0;w<n;w++)
            if(visited[w]==0&&d[w]<min)
            {
                v=w;
                min=d[w];
            }
        visited[v]=1;//加入集合S
    }
    for(i=0;i<n;i++)
    {
        if(i==v0) continue;
        printf("从%d到%d最短路为 :%d\t",v0,i,d[i]);//倒着输出的,没有颠倒过来。
        next=path[i];
		printf("%d--",i);
        while(next!=path[next])
        {
            printf("%d--",next);
            next=path[next];
        }
		printf("%d\n",v0);
    }
}



 

Floyd算法求最短路

适用范围:可以求任意两点之间最短路,效果等同n次循环用Dijkstra算法,但是形式上比Dijkstra算法要简单的多。并且边权可正可负,对于稠密图,效率高于Dijkstra

算法描述:(可求最短路径及路线)

说明:path[N][N]是存储路径的二维数组,path[i][j]存的是i到j的最短路径上面终点j之前的那个节点的编号,map[N][N]存储的是更新的距离关系,arcs[N][N]存储的是原始的图的邻接矩阵。

void floyd(int n)
{  
    int i,j,next,k;
    for(i=0;i<n;i++)
        for(j=0;j<n;j++)//初始化map和path
    	{ 
    		map[i][j]=arcs[i][j];
    		if(i!=j&&map[i][j]<0x7FFFFFFF)//i,j之间有通路
    			path[i][j]=i;
    		else  
                path[i][j]=-1;//不可通 
    	} 
    for(k=0;k<n;k++)
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
                if (map[i][k]<map[i][j]-map[k][j])//用0x7FFFFFFF的时候小心加法溢出 
                {
                    map[i][j]=map[i][k]+map[k][j];      
                    path[i][j]=path[k][j];//p[i][j]存储的是i到j的最短路径上面终点j之前的那个节点的编号!
                }
    for(i=0;i<n;i++)//输出路径长度及其路径
        for(j=0;j<n;j++) 
            if(i!=j)    
            {
                printf("%d到%d的最短路为:",i,j);
				if(map[i][j]==0x7FFFFFFF)
				{
					printf("无穷大\n");
					continue;
				}
				else printf("%d",map[i][j]);
                next=path[i][j];       
                printf("路径为:%d",j);
                while(next!=i&&next!=-1)//一定要注意,这里搜索到-1的话就说明之间没有路径啊!
                { 
                    printf("←%d",next);   
                    next=path[i][next];
                }
                printf("←%d\n",i);
            }
}


 


 

 
 

 

Bellman-Ford算法求最短路

适用范围:带有负权值得图,如果图中有负权回路,则可以检测图中有无回路。

算法描述:(可求最短路和路径)

说明:map[N][N]为图的邻接矩阵,path[N]为路径存储数组

int BellmanFord(int v0,int n)
{
    int i,j,v,w,next;
    for(i=0;i<n;i++)
    {
        d[i]=map[v0][i];
        if(i!=v0&&d[i]<MAX)
            path[i]=v0;
        else path[i]=-1;
    }
    path[v0]=v0;
    for(i=2;i<n;i++)
    {
        for(w=0;w<n;w++)//更新加入一条边后到各个顶点的距离 
        {
            if(v0!=w)
            {
                for(v=0;v<n;v++)
                    if(map[v][w]+d[v]<d[w])
                    {
                        d[w]=map[v][w]+d[v];
                        path[w]=v;
                    }
            }
        }
    }
    //如果没有负权回路的话,则路径最长为n-1段,但是下面如果发现再加
    //入一条边还能够使路长边短的话,则说明有回路了! 
    for (i=0;i<n;i++)
    {
    	for (j=0;j<n;j++)
    	{
    		if (map[i][j]<MAX&&d[j]>d[i]+map[i][j])
    			return 0;//存在从源点可达的负权值回路
    	}
    }
    for(i=0;i<n;i++)//没有回路则回溯路径 
    {
        if(i==v0) continue;
        printf("从%d到%d最短路为 :",v0,i);
        if(d[i]==MAX)
        {
            printf("无穷大\n");
            continue;
        }
        else printf("%d\t",d[i]); 
        next=path[i];
		printf("路径为:%d--",i);
        while(next!=path[next]&&next!=-1)
        {
            printf("%d--",next);
            next=path[next];
        }
        printf("%d\n",v0);
    }
    return 1;
}

下面是一些测试数据

//无负权值 
6 8
0 2 10
0 4 30
0 5 100
1 2 5
2 3 50
3 5 10
4 3 20
4 5 60
//有负权值,但无负权回路 ,此时计算会出现错误 
7 10
0 1 6
0 2 5
0 3 5
1 4 -1
2 1 -2
2 4 1
3 2 -2
3 5 -1
4 6 3
5 6 3
//有负权回路的图
3 3
0 1 1
1 0 -2
1 2 7



 

你可能感兴趣的:(最短路径常用算法总结)