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