无向图dijkstra算法求最短路径

最短路径 专栏收录该内容
2 篇文章 0 订阅

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

在这里插入图片描述

这算法讲解起来确实有点麻烦,但其实还是比较简单的,有贪心算法的意思,每次都找最小的,再通过最小的找最小的
再有什么不懂的就问我吧,看见了一定会回复的

希望各位程序猿能点个赞!!!

  • 9
    点赞
  • 1
    评论
  • 32
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

评论 1 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:游动-白 设计师:我叫白小胖 返回首页

打赏作者

yogur_father

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值