dijkstra算法求最短路径
这个算法的思想和弗洛伊德算法有一点点相似,都是找一个中转站
1. 准备工作
1.1
我们的目的是要找到起始节点到各个结点的最小值,同时输出路径
因此我们定义几个数据变量
#include<iostream>
#include<string.h>
#define MAX 10000000
using namespace std;
int maps[100][100];
int dis[100];//用来存储点到原点的距离
int flag[100];//用来判断点是否遍历过
string path[100]="";
maps就是两点之间的路径
dis就是两个结点之间的最小距离,最后要进行输出的
flag就是用来标记这个结点是否被遍历过
路径我们储存在path中
1.2
由于我们的路径是储存在path中,在最后输出的路径是string类型,但是结点我设置的是int型
(其实也可以用模板写,这样结点就不一定是int的,我这里就不多写了)
所以我们之后写一个int转string的函数
string i_to_s(int num)
{
int j=0;
char str1[20]="";
string str2="";
while (num > 0)
{
str1[j] = num % 10 + '0';
num = num / 10;
j++;
}
for (int i = strlen(str1) - 1; i >= 0; i--)
{
str2 += str1[i];
}
return str2;
}
c++有一个函数是 to_string(),直接可以把int转为string的,但是好像要设置编译环境,我不太懂这个,如果你的编译环境可以的话,能直接用这个函数,就不用单独写上面这函数了
1.3
初始化这个图
void chushi()
{
int i,j;
memset(flag,-1,sizeof(int)*100); //初始化函数,将flag中的数据变为-1
for (i=0;i<100;i++)
{
for (j=0;j<100;j++)
{
if (i==j)
maps[i][j] = 0;
else
maps[i][j] = MAX;
}
}
}
我们的图是一个无环的简单图,所以在主对角线上全为0
剩下的值我们就附为最大值MAX,至于为什么小学二年级的离散和数据结构课上老师应该会讲的
简单来说就是方便后面进行权值的比较
其次有一点就是如果一个数组全部变为0,我们可以
flag[]={0};
但是
flag[]={-1};
只是给第一个数变成了-1,剩下的还是0,这地方要注意一下
2.核心代码
这个代码参数我们要传入一个起始点x,同时要传入结点数
在传入起始点x后,我们先把x到剩下各个点的路径保存以下,不然在后面的代码中,这个路径会被刷新,保存的路径就是错的(这里不太明白没关系,看到后的代码就明白了)
for(int k=1;k<=n;k++) //我的结点是从1开始的
path[k]=i_to_s(x)+"->"+i_to_s(k);
之后我们将之前的maps的权值赋值给dis
这就是路径长的初始值,因为在进行算法之前两者之间的距离就是maps的值,进行算法后,这个值要更新的
for (i=1;i<=n;i++)
dis[i] = maps[x][i];
同时这个初始值是第一个被遍历的,而且他到自身的最短距离是0
dis[x] = 0;
flag[x] = 1;
----------------------------------------------------------------------------------------------------------------------------------------------
核心代码中的核心就要来了
我们先要从起始点x找这个点到剩下点的最段路径的结点
比如起始点是1,他到2的距离是5,到3的距离是2,那么3就是我们要找的点
找到这个最短距离点就方便我们找其他的最短距离了
同时这个结点就算是被遍历过了
flag要变成 1
我之前说过,这个算法还是在找一个中转站,1->3的距离现在是最小的,那我们再看看1->3->j 的距离是否比1->j的距离更小,如果更小的话,我们把1->3->j这个路径存入path[ j ]中,把1->3->j 的距离存入dis[ j ]中
这里path[ j ]和 dis[ j ] 我都用的一维数组,代表起始点到结点 j 的关系
值得注意的是这里的结点 j 我们还不能标记为被遍历过
因为我们如果要找所有节点的关系的话,这个过程就要放在一个 for 循环中
如果结点 j 被标记了,那进入以下一次循环后,先找离起始点最近的结点,这个时候 j 被标记了
我们就不能找 1->3->j->i 的这条线了(i 表示剩下的结点)
之前找离起始点最近的结点,就是为了找通过这个结点中转,到其他结点的最短路径
现在 j 被标记了,那么这条路就废了
下面放上代码
for (i=1;i<=n;i++)
{
int min = MAX;
for (j=1;j<=n;j++) //寻找离起始点最小的点
{
if (flag[j]==-1 && min > dis[j])
{
min = dis[j]; //记录最小值
k = j; // 记录最小值的点
}
}
flag[k] = 1;//标记最小值的点 该点距离起始点已是最短距离
for (j=1;j<=n;j++)
{
if (flag[j]!=1&&dis[k]+maps[k][j] < dis[j])
{
dis[j] = min + maps[k][j];
path[j]=path[k]+"->"+i_to_s(j);
}
}
}
}
上面说的有点抽象,我们看一个具体的例子:
起始点为 1
先找最近点,为3
然后通过 3 中转,发现 1->3->2 比1->2 近
所以dis[2]=3,path[2]是 1->3->2
然后进入下一层循环
发现 2 是最近点(1->2是3,1->5是10,1->4是MAX,3被标记过了)
然后发现通过这个 2,我们到 5 的距离是 5 (就是2+1+2),比1->5 的 10 小
所以dis[5]=5,path[5]=1->3->2->5
剩下的同理
如果我们在上例中
1->3->2的时候 就将 2 标记了,那么1->3->2->5 这条路径就被省略了
3.完整代码
#include<iostream>
#include<string.h>
#define MAX 10000000
using namespace std;
int maps[100][100];
int dis[100];//用来存储点到原点的距离 假设 点由数字 1-100 表示
int flag[100];//用来判断点是否计算过
string path[100]="";
string i_to_s(int num)
{
int j=0;
char str1[20]="";
string str2="";
while (num > 0)
{
str1[j] = num % 10 + '0';
num = num / 10;
j++;
}
for (int i = strlen(str1) - 1; i >= 0; i--)
{
str2 += str1[i];
}
return str2;
}
void chushi()
{
int i,j;
memset(flag,-1,sizeof(int)*100);
for (i=0;i<100;i++)
{
for (j=0;j<100;j++)
{
if (i==j)
maps[i][j] = 0;
else
maps[i][j] = MAX;
}
}
}
void dijkstra(int x,int n) //X 表示 起始点 ,n表示点的数量
{
int i,j,k;
for(int k=1;k<=n;k++)
path[k]=i_to_s(x)+"->"+i_to_s(k);
for (i=1;i<=n;i++)
dis[i] = maps[x][i];
dis[x] = 0;
flag[x] = 1;
for (i=1;i<=n;i++)
{
int min = MAX;
for (j=1;j<=n;j++) //寻找离起始点最小的点
{
if (flag[j]==-1 && min > dis[j])
{
min = dis[j]; //记录最小值
k = j; // 记录最小值的点
}
}
flag[k] = 1;//标记最小值的点 该点距离起始点已是最短距离
for (j=1;j<=n;j++)
{
if (flag[j]!=1&&dis[k]+maps[k][j] < dis[j])
{
dis[j] = min + maps[k][j];
path[j]=path[k]+"->"+i_to_s(j);
}
}
}
}
int main ()
{
int i,n,m,a,b,c,x;//n表示点的个数 m表示边的个数,a,b 存储点 ,c存储边的权值,x为起始点
cout<<"输入点数和边数:"<<endl;
cin>>n>>m;
chushi(); //初始化
cout<<"输入两点及之间的距离:"<<endl;
for (i=0;i<m;i++)
{
cin>>a>>b>>c;
maps[a][b] = c;
maps[b][a] = c;
}
cout<<"输入起始点:"<<endl;
cin>>x;
dijkstra(x,n);
cout<<endl;
for (i=1;i<=n;i++)
{
cout<<x<<"->"<<i<<"最短距离为:"<<dis[i]<<endl;
cout<<path[i];
cout<<endl;
}
}
还用上面的例子
1 2 5
1 3 2
1 5 10
2 5 2
2 3 1
3 4 3
这算法讲解起来确实有点麻烦,但其实还是比较简单的,有贪心算法的意思,每次都找最小的,再通过最小的找最小的
再有什么不懂的就问我吧,看见了一定会回复的
希望各位程序猿能点个赞!!!