最短路径问题:
一、源点到其余顶点的最短路径
二、每一对顶点之间的最短路径
一、迪杰斯特拉算法(Dijkstra)
Dijkstra: 解决单源最短路径,按路径长度递增的次序产生最短路径的算法。*主要找出当前源点到其他顶点最短的一条路径,再利用此路径更新源点至各顶点的最小路径,循环往复直至源点到各顶点均为最短路径。
定义邻接矩阵及辅助数组
#include<stdbool.h>
#define MAXSIZE 100
#define INF 0x3f3f3f3f
//邻接矩阵
typedef struct
{
char vexs[MAXSIZE];
int arcs[MAXSIZE][MAXSIZE];
int vexnum,arcnum;
}AMGraph;
bool S[MAXSIZE];//是否确定最短路径长度
int D[MAXSIZE];//记录当前最短路径长度
int P[MAXSIZE];//终点直接前驱顶点序号
Dijkstra算法
void Dijkstra(AMGraph G,int v)
{
int n=G.vexnum,i,j,l,min;
for(i=0;i<n;i++)
{
S[i]=false;
D[i]=G.arcs[v][i];
if(D[i]<INF)
{
P[i]=v;
}
else
{
P[i]=-1;
}
}
S[v]=true;
D[v]=0;
for(i=0;i<n;i++)
{
min=INF;
for(j=0;j<n;j++)
{
if(D[j]<min&&!S[j])
{
l=j;
min=D[j];
}
}
S[l]=true;
for(j=0;j<n;j++)
{
if((D[l]+G.arcs[l][j])<D[j]&&!S[j])
{
D[j]=D[l]+G.arcs[l][j];
P[j]=l;
}
}
}
return ;
}
时间复杂度:O(n^2)
二、弗洛伊德算法(Floyd)
Floyd:解决多源最短路径,形式上比Dijkstra更容易理解,因此更适合解多源最短路径。 依次利用各个顶点进行中转来不断各个更新各个顶点之间最小路径,直至各个顶点之间均为最短路径即可。
定义邻接矩阵及辅助数组
#include<stdbool.h>
#define MAXSIZE 100
#define INF 0x3f3f3f3f
//邻接矩阵
typedef struct
{
char vexs[MAXSIZE];
int arcs[MAXSIZE][MAXSIZE];
int vexnum,arcnum;
}AMGraph;
int P[MAXSIZE][MAXSIZE];//最短路径上顶点的前一顶点的序号
int D[MAXSIZE][MAXSIZE];//记录顶点之间的最短路径长度
Floyd算法
void Floyd(AMGraph G)
{
for(int i=0;i<G.vexnum;i++)
{
for(int j=0;j<G.vexnum;j++)
{
D[i][j]=G.arcs[i][j];
if(D[i][j]<INF&&i!=j)
{
P[i][j]=i;
}
else
{
P[i][j]=-1;
}
}
}
for(int k=0;k<G.vexnum;k++)
{
for(int i=0;i<G.vexnum;i++)
{
for(int j=0;j<G.vexnum;j++)
{
if(D[i][k]+D[k][j]<D[i][j])
{
D[i][j]=D[i][k]+D[k][j];
P[i][j]=P[k][j];
}
}
}
}
return ;
}
时间复杂度:O(n^3)
三、贝尔曼福特算法(Bellman-Ford)
Bellman-Ford: 解决负权单源最短路径,其本质是动态规划思想。*主要是对图进行最多V-1次松弛操作,得到源点到各点所有可能的最短路径,遍历都结束后,若再进行一次遍历,还能得到s到某些节点更短的路径的话,则说明存在负环路,所以还能够用来检测负环。BellmanFord支持解决负权路,没有负权也可以用,不过效率比Dijkstra低很多,所以更适合解决负权问题。
定义邻接矩阵及辅助数组
#include<stdbool.h>
#define MAXSIZE 100
#define INF 0x3f3f3f3f
//邻接矩阵
typedef struct
{
char vexs[MAXSIZE];
int arcs[MAXSIZE][MAXSIZE];
int vexnum,arcnum;
}AMGraph;
int D[MAXSIZE];//记录当前最短路径长度
int P[MAXSIZE];//终点直接前驱顶点序号
Bellman-Ford算法
void BellmanFord(AMGraph G,int v)
{
int n=G.vexnum;
for(int i=0;i<n;i++)
{
D[i]=G.arcs[v][i];
if(D[i]<INF)
{
P[i]=v;
}
else
{
P[i]=-1;
}
}
D[v]=0;
for(int i=2;i<G.vexnum-1;i++)
{
for(int j=0;j<G.vexnum;j++)
{
for(int k=0;k<G.vexnum;k++)
{
if((D[j]+G.arcs[j][k])<D[k])
{
D[k]=D[j]+G.arcs[j][k];
P[k]=j;
}
}
}
}
bool flag=true;
for(int i=0;i<G.vexnum-1;i++)
{
for(int j=0;j<G.vexnum-1;j++)
{
if(D[i]+G.arcs[i][j]<D[j])
{
flag=false;
break;
}
}
}
return flag;
}
时间复杂度:O(V*E) V(顶点数),E(边数)
DFS和BFS算法也可以求最短路径,但只能用于求无权图。
最短路线
利用最短路径的Path数组回溯出两点间最短路径的整条路线
邻接矩阵存储图:
void BeforePath(int a,int b)//参数a起点,参数b终点,根据最短路径终点前驱数组回溯路径上所有地点
{
if (P[a][b] == -1)
{
return;
}
else if (P[a][b] == a)
{
printf("->");
printf("%s", Site[b].name);
return;
}
else
{
BeforePath(a, P[P[a][b]][b]);
printf("->");
printf("%s", Site[b].name);
return;
}
}
输出的路径未包括最初的起点,可以在函数外加上最初起点的输出
所有路径
利用DFS算法,借助辅助数组(或栈)输出两点间所有路径
邻接矩阵存储图:
void DFS(int a, int b,int A)//参数a为起点,参数b为终点,参数A为最初的起点,深度搜索寻找两点间所有路径
{
for (int i = 1; i <= L; i++)
{
if (S[a][i] < INF && V[i] == 0)
{
if (i == b)
{
int j;
printf("%s->", Site[A].name);
for (j = 0; j < E; j++)
{
printf("%s->", Site[T[j]].name);
}
printf("%s\n\n",Site[b].name);
F++;
}
else
{
V[i] = 1;
T[E] = i;
E++;
DFS(i, b,A);
V[i] = 0;
E--;
}
}
}
}
为了让路线更完整,多设置了一个参数A即最初的起点,递归时参数A不变,从而输出完整路径