先来讲个段子:为什么 Dijkstra 不能提出 floyd 算法?因为他的名字是 ijk 而不是 kij。
get不到点没有关系。我们今天的任务是看懂这个笑话。
dijkstra 的效率是n^2.处理的是一个点到所有点的最短路径。而floyd效率n^3,处理的是任意点的最短距离。dijkstra如果要处理任意点。
Dijkstra单源最短路算法(无法解决带负边问题): 单源就是由一到多,或由多到一,就是起点和终点至少有一个为一,若均大于1则为多源,求多源最短路时使用Dijkstra算法需要进行多次操作。 普通: 图的建立(就是把输入的路线储存起来):用邻接矩阵。 首先要进行初始化,将每个点到起点的距离设为一个较大的数,并将所有的点都标记为未访问过。 就是由起点开始,标记起点已经访问过,并将起点到起点的距离更新为0,遍历每一个可以走到的点,更新他们到起点的距离,然后找出未访问过的距离起点最近的点,因为它已经是为访问过的点中最近的点,所以从别的点来到这一点一定会比现在要远,所以它此时已经是最近的点了,然后再把它看作是出发点,再次进行上述操作(在更新距离是要先判断新距离是否比久距离要来得近,若比原来的远则不更新距离),最后没有未访问过的点或剩余点到出发点的距离均为无穷远则结束操作。 在更新距离时可以顺便更新前驱,就能够记录路径,不过一般题目都会有多条路径,在记录时要注意题目要求。 时间复杂度:O(n^2) ,因为要先对每个节点进行遍历,更新路径,然后还要找出所有未访问节点中最近的点,最差的情况就是要将n个点均进行操作,操作次数就是(2n)*n。 优先队列优化(堆优化): 优化主要就是缩短了查找未被访问的节点中最近的节点的时间,用优先队列来完成。 从起点出发遍历所有点,若距离缩短则将其加入队列中,结束后队列首则为下一点。当队列首元素中保存的点和存入时的距离大于此时该点对应距离时,将其跳过。 Floyd多源最短路径算法(无法解决带负边问题): 有一些动态规划的思想,利用中介点。dist[i][j] = min(dist[i][k]+dist[k][j],dist[i][j]); 当有下一点l 作为中介点时,A到B的距离就有五种种选择设上一中介点为C 这一中介点为D。A-B A-C-B A-D-B A-D-C-B A-C-D-B 。而D-C-B和A-C-D在上次以C为中介点就已经算出,D-B = min(D-C-B,D,B),A-C-D同理,然后就是不断的重复,把所有点做中介点一次就完成了。 核心代码: for(int k=1;k<=n;i++) { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { dist[i][j] = min(dist[i][k]+dist[k][j],dist[i][j]); } } }
int dijkstra(Mgraph g,int path[],int dis[],int v) { int set[maxSize]; for(int i=0;i<g.n;i++) { set[i]=0; dis[i]=g.edge[v][i]; if(g.edge[v][i]<inf) path[i]=v; else path[i]=-1; } set[v]=1; path[v]=-1; for(int i=0;i<g.n-1;i++) { int k=-1,min=inf; for(int j=0;j<g.n;j++) { if(dis[j]<min&&set[j]==0) { min=dis[j]; k=j; } } set[k]=1; for(int j=0;j<g.n;j++) { if(set[j]==0&&dis[j]>dis[k]+g.edge[k][j]) { dis[j]=dis[k]+g.edge[k][j]; path[j]=k; } } } } void printPath(int u,int path[]) { int stack[maxSize]; int top=-1; while(path[u]!=-1) { stack[++top]=u;//这个是是u不是path[u],终点也要进来。 u=path[u]; } stack[++top]=u;//最后一个==-1的点 while(top!=-1) { cout<<stack[top--]<<" "; cout<<endl; } }
void floyd(Mgraph g,int path[][maxSize]) { int map[maxSize][maxSize]; for(int i=0;i<g.n;i++) for(int j=0;j<g.n;j++) { map[i][j]=g.edge[i][j]; path[i][j]=-1; } for(int k=0;k<g.n;k++) for(int i=0;i<g.n;i++) for(int j=0;j<g.n;j++) { if(map[i][j]>map[i][k]+map[k][j]) { map[i][j]=map[i][k]+map[k][j]; path[i][j]=k; } } } void printfpath(int u,int v,int path[][maxSize]) { if(path[u][v]==-1) cout<<u<<"——"<<v<<" "; else { int mid=path[u][v]; printfpath(u,mid,path); printfpath(mid,v,path); } }
这就是为什么folyd为什么是kij的循环。