最短路径之Folyd算法

简介: 最短路径之Folyd算法

Dijkstra算法是求源点到其它各个顶点的最短路径,如果求解任意两个顶点的最短路径,则需要以每个顶点为源点,重复调用n次Dijkstra算法。完全没必要这么麻烦,下面介绍的Floyd算法可以求解任意两个顶点的最短路径。Floyd 算法又称为插点法,其算法核心是在顶点i到顶点j之间,插入顶点k,看是否能够缩短i和j之间距离(松弛操作)。


算法步骤:


数据结构。 设置地图的带权邻接矩阵为G.Edge[],即如果从顶点i到顶点j有边,

就让G.Edge[jJi]=<i,j>的权值,否则G.Edge [j]i]=∞(无穷大);采用两个辅助数组:

最短距离数组dist[i][i],记录从i到j顶点的最短路径长度;前驱数组p[i][i],记录

从i到j顶点的最短路径上i顶点的前驱。

初始化。 初始化dist[i][i]= G.Edge[i][i],如果顶点i到顶点j有边相连,初始化p[i][i]=i,

否则p[i][i]=-1。

插点。其是就是在i,j 之间插入顶点k,看是否能够缩短i和j之间距离(松弛操作)。

如果dist[i][j]>dist[i][k]+dist[k][i],则dist[i][i]=dist[i][k]+dist[k][],记录顶点j的前驱

为: p[i][j]=p[k][j]。


20210517215210366.png


如上图所示:

  1. 初始化:
    通过邻接矩阵对dist和p进行初始化


20210517214459483.png

插点,也就是插入某个点k后其他点i依靠k到达j的距离更短.如果一个节点的出度指向k则可进行借点(插点)操作.这里我们使用三个for循环进行实现,当dist[i][j]>dist[i][k]+dist[k][i] 进行更新dist和p数组.            每一步的结果如下:

将k = 0,进行插点,结果为:

20210517214609132.png

将k = 1进行插点,结果为:

20210517214658434.png


将k = 2进行插点,结果为:

20210517214726231.png

将k = 3 进行插点,结果为:

20210517214819641.png

插点结束后。dist[][]数组即为各顶点之间的最短距离,如果想找顶点i到顶点j的最短路径,可以根据前驱数组p获得。例如求1到2的最短路径,首先读取p[1][2]=3,说明顶点2的前驱为3,继续向前找,读取p[1][3]=1,说明3的前驱为1,得到1结束.到2的最短路径为1-- 3-- 2; 求1到0的最短路径,首先读取p[1][0]=2,说明顶点0的前驱为2,继续向前找,读取p[1][2]=3,说明2的前驱为3,继续向前找,读取p[1][3]=1,得到1到0的最短路径为1-- 3-- -2–0;


时间复杂度:三层for语句循环,时间复杂度为0(n^3)。

空间复杂度: 采用两个辅助数组:最短距离数组dist[i][j]和前驱数组p[i][j],因此空间复杂度为O(n^2)。


尽管Floyd算法的时间复杂度为O(n^3),但其代码简单,对于中等输入规模来说,仍然相当有效。如果用Dijkstra算法求解各个顶点之间的最短路径,则需要以每个顶点为源点调用一次,一共调用n次,其总的时间复杂度也为O(n3)。特别注意的是,Dijkstra 算法无法处理处理带负权值边的图,Floyd 算法可以处理带负权值边的图,但是不允许图中包含带负权值边组成的回路。


参考代码

#include <iostream>
#include<cstring>
#include<windows.h>
using namespace std;
#define MaxVnum 100  //顶点数最大值
const int INF=1e7; // 无穷大10000000
typedef string VexType;  //顶点的数据类型,根据需要定义
typedef int EdgeType;  //边上权值的数据类型,若不带权值的图,则为0或1
typedef struct {
  VexType Vex[MaxVnum];
  EdgeType Edge[MaxVnum][MaxVnum];
  int vexnum,edgenum; //顶点数,边数
} AMGragh;
int dist[MaxVnum][MaxVnum],p[MaxVnum][MaxVnum];
int locatevex(AMGragh G,VexType x) {
  for(int i=0; i<G.vexnum; i++) //查找顶点信息的下标
    if(x==G.Vex[i])
      return i;
  return -1;//没找到
}
void CreateAMGraph(AMGragh &G) { //创建无向图的邻接矩阵
  int i,j,w;
  VexType u,v;
  cout << "请输入顶点数:"<<endl;
  cin>>G.vexnum;
  cout << "请输入边数:"<<endl;
  cin>>G.edgenum;
  cout << "请输入顶点信息:"<<endl;
  for(int i=0; i<G.vexnum; i++) //输入顶点信息,存入顶点信息数组
    cin>>G.Vex[i];
  for(int i=0; i<G.vexnum; i++) //初始化邻接矩阵所有值为0,若是网,则初始化为无穷大
    for(int j=0; j<G.vexnum; j++)
      if(i!=j)
        G.Edge[i][j]=INF;
      else
        G.Edge[i][j]=0; //注意i==j时,设置为0
  cout << "请输入每条边依附的两个顶点及权值:"<<endl;
  while(G.edgenum--) {
    cin>>u>>v>>w;
    i=locatevex(G,u);//查找顶点u的存储下标
    j=locatevex(G,v);//查找顶点v的存储下标
    if(i!=-1&&j!=-1)
      G.Edge[i][j]=w; //有向图邻接矩阵存储权值
  }
}
void Floyd(AMGragh G){
  //int i,j,k;
  for(int i = 0; i < G.vexnum; i++){
    for(int j = 0; j < G.vexnum; j++){
      dist[i][j] = G.Edge[i][j];//有路径则初始化为路径长 
      if(dist[i][j] < INF && i!= j){
        p[i][j] = i;// 如果i-->j之间有路径,则将j的前驱置为i 
      }else{
        p[i][j] = -1;//无路径则置为-1 
      }
    }
  }
  for(int k = 0; k <G.vexnum; k++){// 依次插入 k节点  更新最短路径dist和前驱数组p 
    for(int i = 0; i < G.vexnum; i++){
      for(int j = 0; j < G.vexnum; j++){
        if(dist[i][k]+dist[k][j]<dist[i][j]){//如果i经k到j有更短的路径 
          dist[i][j] = dist[i][k]+dist[k][j];//更新dist[i][j]
          p[i][j] = p[k][j];//更改j的前驱为p[k][j]  不能直接更新为k 因为k-->j不一定是直达的,中间可能还有其他节点. 
        }
      }
    }
  } 
}
void print(AMGragh G){
  for(int i = 0; i < G.vexnum; i++){//输出最短距离数组 
    for(int j = 0; j < G.vexnum; j++){
      cout<<dist[i][j]<<"\t";
    }
    cout<<endl;
  }
  cout<<endl;
  for(int i = 0; i < G.vexnum; i++){//输出前驱数组 
    for(int j = 0; j < G.vexnum; j++){
      cout<<p[i][j]<<"\t";
    }
    cout<<endl;
  }
}
void DisplayPath(AMGragh G,int s,int t){//显示最短路径 
  if(p[s][t]!=-1){
    DisplayPath(G,s,p[s][t]);
    cout<<G.Vex[p[s][t]]<<"-->";
  }
} 
int main()
{
  VexType start,destination;
  int u,v;
  AMGragh G;
  CreateAMGraph(G);
  Floyd(G);
  print(G);
  cout<<"请依次输入路径的起点和终点名称:";
  cin>>start>>destination;
  u = locatevex(G,start);
  v = locatevex(G,destination);
  DisplayPath(G,u,v);
  cout<<G.Vex[v]<<endl;
  cout<<"最短路径的长度为:"<<dist[u][v]<<endl;
  cout<<endl; 
  return 0;
 } 
 /*
0 1 1
0 3 4
1 2 9
1 3 2
2 0 3
2 1 5
2 3 8
3 2 6
 */

运行结果(就是例子那幅图):

20210517220449759.jpg



相关文章
|
2月前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
92 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
4月前
|
算法 定位技术
路径规划算法 - 求解最短路径 - A*(A-Star)算法
路径规划算法 - 求解最短路径 - A*(A-Star)算法
149 1
|
4月前
|
自然语言处理 算法
HanLP — HMM隐马尔可夫模型 - 路径规划算法 - 求解最短路径 - 维特比(Viterbi)算法
HanLP — HMM隐马尔可夫模型 - 路径规划算法 - 求解最短路径 - 维特比(Viterbi)算法
67 0
HanLP — HMM隐马尔可夫模型 - 路径规划算法 - 求解最短路径 - 维特比(Viterbi)算法
|
5月前
|
算法 Java
Java语言实现最短路径算法(Shortest Path)
Java语言实现最短路径算法(Shortest Path)
70 3
|
4月前
|
算法
路径规划算法 - 求解最短路径 - Dijkstra(迪杰斯特拉)算法
路径规划算法 - 求解最短路径 - Dijkstra(迪杰斯特拉)算法
104 0
|
6月前
|
存储 算法 C语言
数据结构学习记录——图-最短路径问题(无权图单源最短路径算法、有权图单源最短路径算法、多源最短路径算法、Dijkstra(迪杰斯特拉)算法、Floyd算法)
数据结构学习记录——图-最短路径问题(无权图单源最短路径算法、有权图单源最短路径算法、多源最短路径算法、Dijkstra(迪杰斯特拉)算法、Floyd算法)
112 1
|
6月前
|
算法 Java
Java数据结构与算法:最短路径算法
Java数据结构与算法:最短路径算法
|
6月前
|
算法 Java 定位技术
Java数据结构与算法:贪心算法之最短路径
Java数据结构与算法:贪心算法之最短路径
|
6月前
|
算法 定位技术
探寻最短路径之谜:Dijkstra算法详解
探寻最短路径之谜:Dijkstra算法详解
|
6月前
|
算法
【经典LeetCode算法题目专栏分类】【第4期】BFS广度优先算法:单词接龙、最小基因变化、二进制矩阵中的最短路径
【经典LeetCode算法题目专栏分类】【第4期】BFS广度优先算法:单词接龙、最小基因变化、二进制矩阵中的最短路径