Dijkstra算法是解决单源最短路径问题的贪心算法,它先求出长度最短的一条路径,再参照该路径求出长度次短的一条路径,直到求出从源点到其他各个顶点的最短路径
算法步骤
1、数据结构
设置地图的带权邻接矩阵G.Edge[j],即如果从源点u到i有边就令G.Edge[u][i]等于<u,i>的权值,则G.Edge[u][i]=∞,采用一位数组dist[i]来记录从源点到i顶点的最短路径长度,采用一维数组p[i]来记录最短路径上i顶点的前驱
2、初始化
令集合S={u},对于集合V-S中的所有顶点x,初始化dist[i]=G.Edge[u][i],如果源点u到顶点i右边相连,初始化p[i]=u,否则p[i]=-1
3、找最小
在集合V-S中依照贪心策略来寻找使得dist[j]具有最小值的顶点t,即dist[t]=min(dist[j]j属于V-S集合),则顶点t就是集合V-S中距离源点u最近的顶点
3、加入S战队
将顶点t加入集合S中,同时更新V-S
4、判结束
如果集合V-S为空,算法结束
5、借东风
在第3步中已经找到了源点到t的最短路径,那么对集合V-S中
所有与顶点t相邻的顶点j,都可以借助t走捷径,如果dist[j]>dist[t]+G.Edge[t][j],则dist[j]=dist[t]+G.Edge[t][j],记录顶点j的前驱为t,有p[j]=t,转到第3步
样例步骤
1 2 3 4 5
2 5 ∞ ∞ dist[] 0 2 5 ∞ ∞
∞ 2 6 ∞
∞ ∞ 7 1 1 2 3 4 5
∞ ∞ ∞ 2 ∞ 4 p[] -1 1 1 -1 -1
∞ ∞ -S={2,3,4,5}
∞ ∞ ∞ ∞ ∞ V
[2]最小,入战队V-S={2,3,4,5} 借东风
dist
1 2 3 4 5
2 5 ∞ ∞ dist[] 0 2 4 8 ∞
∞ 2 6 ∞
∞ ∞ 7 1 1 2 3 4 5
∞ ∞ ∞ 2 ∞ 4 p[] -1 1 2 2 -1
∞ ∞ -S={3,4,5}
∞ ∞ ∞ ∞ ∞ V
[3]最小,入战队V-S={4,5} 借东风
dist
1 2 3 4 5
2 5 ∞ ∞ dist[] 0 2 4 8 4
∞ 2 6 ∞
∞ ∞ 7 1 1 2 3 4 5
∞ ∞ ∞ 2 ∞ 4 p[] -1 1 2 2 3
∞ ∞ -S={4,5}
∞ ∞ ∞ ∞ ∞ V
[4]最小,入战队V-S={5} 借东风
dist
1 2 3 4 5
2 5 ∞ ∞ dist[] 0 2 4 8 4
∞ 2 6 ∞
∞ ∞ 7 1 1 2 3 4 5
∞ ∞ ∞ 2 ∞ 4 p[] -1 1 2 2 3
∞ ∞ -S={5}
∞ ∞ ∞ ∞ ∞ V
[5]最小,如战队V-S={} 结束算法 dist
代码实现
//main1.cpp
#include<iostream>
#include <cstring>
#include <stack>
#include <climits>
using namespace std;
const int MaxVnum = 100; // 城市的个数可修改
const int INF = INT_MAX; // 无穷大10000000
int dist[MaxVnum], p[MaxVnum]; //最短距离和前驱数组
bool flag[MaxVnum]; //如果s[i]等于true,说明顶点i已经加入到集合S;否则顶点i属于集合V-S
typedef string VexType; //顶点的数据类型,根据需要定义
typedef int EdgeType; //边上权值的数据类型,若不带权值的图,则为0或1
typedef struct
{
[MaxVnum];//顶点
VexType Vex[MaxVnum][MaxVnum];
EdgeType Edgeint vexnum, edgenum; //顶点数,边数
} AMGragh;
//根据顶点数据x找到相应顶点的下标
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;
, v;
VexType u<< "请输入顶点数:" << endl;
cout >> G.vexnum;
cin << "请输入边数:" << endl;
cout >> G.edgenum;
cin << "请输入顶点信息:" << endl;
cout for (int i = 0; i < G.vexnum; i++) //输入顶点信息,存入顶点信息数组
>> G.Vex[i];
cin for (int i = 0; i < G.vexnum; i++) //初始化邻接矩阵为无穷大
for (int j = 0; j < G.vexnum; j++)
.Edge[i][j] = INF;
G<< "请输入每条边依附的两个顶点及权值:" << endl;
cout while (G.edgenum--)
{
>> u >> v >> w;
cin = locatevex(G, u); //查找顶点u的存储下标
i = locatevex(G, v); //查找顶点v的存储下标
j if (i != -1 && j != -1)
.Edge[i][j] = w; //有向图邻接矩阵
Gelse
{
<< "输入顶点信息错!请重新输入!" << endl;
cout .edgenum++; //本次输入不算
G}
}
}
//单源最短路径算法
void Dijkstra(AMGragh G, int u)
{
for (int i = 0; i < G.vexnum; i++)
{
[i] = G.Edge[u][i]; //初始化源点u到其他各个顶点的最短路径长度
dist[i] = false;
flagif (dist[i] == INF)
[i] = -1; //源点u到该顶点的路径长度为无穷大,说明顶点i与源点u不相邻
pelse
[i] = u; //说明顶点i与源点u相邻,设置顶点i的前驱p[i]=u
p}
[u] = 0;
dist[u] = true; //初始时,集合S中只有一个元素:源点u true代表加入到S
flagfor (int i = 0; i < G.vexnum; i++)
{
int temp = INF, t = u;
for (int j = 0; j < G.vexnum; j++) //在集合V-S中寻找距离源点u最近的顶点t
if (!flag[j] && dist[j] < temp)
{
= j;
t = dist[j];
temp }
if (t == u)
return; //找不到t,则也代表V-S为空跳出循环
[t] = true; //否则,将t加入集合
flagfor (int j = 0; j < G.vexnum; j++) //更新与t相邻接的顶点到源点u的距离
if (!flag[j] && G.Edge[t][j] < INF)//为临时顶点的出度
if (dist[j] > (dist[t] + G.Edge[t][j]))
{
[j] = dist[t] + G.Edge[t][j];//借东风
dist[j] = t;//更改前驱
p}
}
}
//根据前驱数组寻找路径
void findpath(AMGragh G, VexType u)
{
int x;
<int> S;
stack<< "源点为:" << u << endl;
cout for (int i = 0; i < G.vexnum; i++)//找起点到每个顶点的最短路径
{
= p[i];//目标顶点的前驱
x if (x == -1 && u != G.Vex[i])
{
<< "源点到其它各顶点最短路径为:" << u << "--" << G.Vex[i] << " sorry,无路可达" << endl;
cout continue;
}
while (x != -1)
{
.push(x);//入栈
S= p[x];//向前找前驱
x }
<< "源点到其它各顶点最短路径为:";
cout while (!S.empty())//弹出最短路径
{
<< G.Vex[S.top()] << "--";
cout .pop();
S}
<< G.Vex[i] << " 最短距离为:" << dist[i] << endl;
cout }
}
int main()
{
; //邻接矩阵存储图
AMGragh Gint st;
;
VexType u(G); //初始化图信息
CreateAMGraph<< "请输入源点的信息:" << endl;
cout >> u;
cin = locatevex(G, u); //查找源点u的存储下标
st (G, st); //单源最短路径算法
Dijkstra<< "小明所在的位置:" << u << endl;
cout for (int i = 0; i < G.vexnum; i++)
{
<< "小明:" << u << " - "
cout << "要去的位置:" << G.Vex[i];
if (dist[i] == INF)
<< "sorry,无路可达" << endl;
cout else
<< "最短距离为:" << dist[i] << endl;
cout }
(G, u); //根据前驱数组找最短路径
findpathreturn 0;
}
算法复杂度分析
时间复杂度 O(n^2)
空间复杂度 O(n)
Floyd算法可以求解任意两个顶点的最短路径,Floyd算法又称插心法,其算法核心是在顶点i到顶点j之间,插入顶点k,看是否能够缩短i和j之间的距离(松弛操作)
算法步骤
1、数据结构
设置地图的带权邻接矩阵G.Edge[j],即如果从i到j有边就令G.Edge[i][i]等于<i,j>的权值,则G.Edge[i][j]=∞,采用数组dist[i][j]来记录从i顶点到顶点j的最短路径长度,采用数组p[i][j]来记录从i到j顶点的最短路径上i顶点的前驱
2、初始化
初始化dist[i][j]=G.Edge[i][j],如果顶点i到顶点j有边相连,初始化p[i][j]=i,否则p[i][j]=-1
3、插点
,其实就是在i与j之间插入顶点k,看是否能够缩短i和j之间的距离,如果dist[i][j]>dist[i][k]+dist[k][j],则dist[i][j]=dist[i][k]+dist[k][j],记录i,j的前驱为p[i][j]=p[k][j]
代码实现
//main2.cpp
#include<iostream>
#include<cstring>
#include<climits>
using namespace std;
#define MaxVnum 100 //顶点数最大值
const int INF=INT_MAX; // 无穷大10000000
typedef string VexType; //顶点的数据类型,根据需要定义
typedef int EdgeType; //边上权值的数据类型,若不带权值的图,则为0或1
typedef struct{
[MaxVnum];
VexType Vex[MaxVnum][MaxVnum];
EdgeType Edgeint 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;
,v;
VexType u<<"请输入顶点数:"<<endl;
cout>>G.vexnum;
cin<<"请输入边数:"<<endl;
cout>>G.edgenum;
cin<<"请输入顶点信息:"<<endl;
coutfor(int i=0;i<G.vexnum;i++)//输入顶点信息,存入顶点信息数组
>>G.Vex[i];
cinfor(int i=0;i<G.vexnum;i++)//初始化邻接矩阵所有值为0,若是网,则初始化为无穷大
for(int j=0;j<G.vexnum;j++)
if(i!=j)
.Edge[i][j]=INF;
Gelse
.Edge[i][j]=0; //注意i==j时,设置为0
G<<"请输入每条边依附的两个顶点及权值:"<<endl;
coutwhile(G.edgenum--)
{
>>u>>v>>w;
cin=locatevex(G,u);//查找顶点u的存储下标
i=locatevex(G,v);//查找顶点v的存储下标
jif(i!=-1&&j!=-1)
.Edge[i][j]=w; //有向图邻接矩阵存储权值
G}
}
//用Floyd算法求有向网G中各对顶点i和j之间的最短路径
void Floyd(AMGragh G)
{
int i,j,k;
//各对结点之间初始已知路径及距离
for(i=0;i<G.vexnum;i++){
for(j=0;j<G.vexnum;j++)
{
[i][j]=G.Edge[i][j];
distif(dist[i][j]<INF && i!=j)
[i][j]=i; //如果i和j之间有弧,则将j的前驱置为i
pelse p[i][j]=-1; //如果i和j之间无弧,则将j的前驱置为-1
}
}
for(k=0;k<G.vexnum; k++)//插点
for(i=0;i<G.vexnum; i++)//i
for(j=0;j<G.vexnum; j++)//j
//从i经k到j的一条路径更短
if(dist[i][k]!=INF&&dist[k][j]!=INF&&dist[i][k]+dist[k][j]<dist[i][j])
{
[i][j]=dist[i][k]+dist[k][j]; //更新dist[i][j]
dist[i][j]=p[k][j]; //更改j的前驱为k
p}
}
void print(AMGragh G)
{
int i,j;
for(i=0;i<G.vexnum;i++)//输出最短距离数组
{
for(j=0;j<G.vexnum;j++)
<<dist[i][j]<<"\t";
cout<<endl;
cout}
<<endl;
coutfor(i=0;i<G.vexnum;i++)//输出前驱数组
{
for(j=0;j<G.vexnum;j++)
<<p[i][j]<<"\t";
cout<<endl;
cout}
}
//递归形式,同理也是利用栈的性质,逆推且正向输出
void DisplayPath(AMGragh G,int s,int t )//显示最短路径
{
if(p[s][t]!=-1)
{
(G,s,p[s][t]);
DisplayPath<<G.Vex[p[s][t]]<<"-->";
cout}
}
int main()
{
,destination;
VexType startint u,v;
;
AMGragh G(G);
CreateAMGraph(G);
Floyd(G);
print<<"请依次输入路径的起点与终点的名称:";
cout>>start>>destination;
cin=locatevex(G,start);
u=locatevex(G,destination);
v(G,u,v);
DisplayPath<<G.Vex[v]<<endl;
cout<<"最短路径的长度为:"<<dist[u][v]<<endl;
cout<<endl;
coutreturn 0;
}
算法复杂度分析
时间复杂度O(n^3),Floyd是一个暴力枚举的算法,尝试了尽有的可能,保留最优的
空间复杂度O(n^2)
Dijkstra算法无法处理带负权值边的图,Floyd算法可以处理带负权值边的图,但是不允许图中包含负圈(权值为负的圈),其他解决负权值边的最短路径算法Bellman-Ford和SPFA算法