Dijkstra算法(C/C++)

一、简介:

Dijkstra算法算是贪心思想实现的,首先把起点到所有点的距离存下来找个最短的,然后松弛一次再找出最短的,所谓的松弛操作就是,遍历一遍看通过刚刚找到的距离最短的点作为中转站会不会更近,如果更近了就更新距离,这样把所有的点找遍之后就存下了起点到其他所有点的最短距离。

二、步骤:

一个包含9条边6个点的有向图如下:

将图用二维数组进行存储:

此时再用一个dis数组存储算法目前阶段节点1到各个节点的最短的距离。(算法是从终点向起点倒推的,所以存储的是目前阶段各个节点到节点6最短距离):

每个点松弛计算,直至推出最后结果:

三、算法代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>

#pragma warning(disable:4996)

#define MAX 10
#define Inf 0x3f3f3f3f//memset赋值无穷大的好选择,笔记有解释 

using namespace std;//使用<iostream>的时候,该头文件没有定义全局命名空间,必须使用namespace std,这样才能正确使用cout 

int map[MAX][MAX];
int vis[MAX], dis[MAX];
int n, m;//n个点,m条边

void Init()//初始化二维数组 
{
	memset(map, Inf, sizeof(map));
	for (int i = 1; i <= n; i++)
	{
		map[i][i] = 0;
	}
}

void Getmap()
{
	int u, v, w;
	printf("请依次输入欲计算的图信息,化为二维数组后的行数、列数以及权值信息,空格分开,回车换组\n");
	for (int t = 1; t <= m; t++)
	{
		printf("第%d组数据:\n", t);
		scanf("%d%d%d", &u, &v, &w);
		if (map[u][v]>w)
		{
			map[u][v] = w;
			map[v][u] = w;
		}
	}

}

void Dijkstra(int u)
{
	memset(vis, 0, sizeof(vis));
	for (int t = 1; t <= u; t++)
	{
		dis[t] = map[u][t];
	}
	vis[u] = 1;
	printf("最短路径为:%d\n",u);
	for (int t = 1; t<u; t++)
	{
		int minn = Inf, temp;
		for (int i = 1; i <= u; i++)
		{
			if (!vis[i] && dis[i]<minn)
			{
				minn = dis[i];
				temp = i;
			}
		}
		vis[temp] = 1;
		printf("<-%d", temp);//temp记录当前最短路径节点
		for (int i = 1; i <= u; i++)
		{
			if (map[temp][i] + dis[temp]<dis[i])
			{
				dis[i] = map[temp][i] + dis[temp];
			}
		}
	}

}

int main()
{
	printf("请输入边数和点数,空格分开:\n");
	scanf("%d%d", &m, &n);
	Init();
	Getmap();
	Dijkstra(n);
	printf("\n最短路径长为%d\n", dis[1]);

	return 0;
}

四、结果:

五、笔记:

1.C语言指针的提醒。指针指向存储单元地址,故*a中a存储的是指针指向的地址,故若b=2,要赋值2给*a,便可“*a=b”、“*a=2”或者“a=&b”。但在声明变量时,只能“int *a=&b”,这是因为声明时“int *”实则为一体,所以需要将存储b的单元地址赋值给a,这需要注意。其次还要注意的便是数组的特殊性,声明一个数组b[n],其中b其实存储的是存储这个数组空间的首地址,和&b[n]和&b[0]值相同,故赋值给指针*a除了直接“*a=b[n]”外,还可以直接“a=b”或者“a=&b[n]”或者“a=&b[0]”。

2.为什么函数有全局变量依旧要传参入函数,是因为当我们此函数调用不多时,还分配个全局变量给这个函数其实非常浪费空间,故尽量设置形参传入是节省空间的好办法。

3.为什么使用0x3f3f3f3f作为无穷大数INF进行赋值?在算法竞赛中,我们常常需要用到一个“无穷大”的值,于是很多程序猿将INF设为0x7fffffff。这是32-bit int的最大值。如果这个无穷大只用于一般的比较(比如求最小值时min变量的初值),那么0x7fffffff确实是一个完美的选择。但是更多情况下,0x7fffffff并不是一个好的选择,比如在最短路径算法中,我们使用松弛操作:

if (d[u]+w[u][v]<d[v]) d[v]=d[u]+w[u][v];

如果u,v之间没有边,那么w[u][v]=INF,如果我们的INF取0x7fffffff,那么d[u]+w[u][v]会溢出而变成负数,我们的松弛操作便出错了!准确来说,0x7fffffff不能满足“无穷大加一个有穷的数依然是无穷大”这个条件,它会变成了一个很小的负数。
        0x3f3f3f3f的十进制是1061109567,是10^9级别的(和0x7fffffff一个数量级),而一般场合下的数据都是小于10^9的,所以它可以作为无穷大使用而不致出现数据大于无穷大的情形。
        同时如果我们想要将某个数组清零,我们通常会使用memset(a,0,sizeof(a)),方便又高效,但是当我们想将某个数组全部赋值为无穷大时,就不能使用memset函数而得自己写循环了,因为memset是按字节操作的,它能够对数组清零是因为0的每个字节都是0(一般我们只有赋值为-1和0的时候才使用它)。现在好了,如果我们将无穷大设为0x3f3f3f3f,那么奇迹就发生了,0x3f3f3f3f的每个字节都是0x3f!所以要把一段内存全部置为无穷大,我们只需要memset(a,0x3f,sizeof(a))。

4.关于运算符优先级,如下图,另所有一元操作符优先级都高于&&等二元操作符:

5.main函数里定义的变量并非全局变量,是局部变量,只能在main函数里使用,main函数里的调用函数都无法使用,因为调用函数是在main以外定义的。

六、版权声明:

本文节选或转载cnblogs博主「black_hole6」的原创文章,依作者要求以下附上原文出处链接。
        原文链接:https://www.cnblogs.com/Staceyacm/p/10782094.html

本文节选或转载CSDN博主「jiange_zh」的原创文章,遵循 CC 4.0 BY-SA 版权协议,以下附上原文出处链接。
        原文链接:https://blog.csdn.net/jiange_zh/article/details/50198097

发布了4 篇原创文章 · 获赞 0 · 访问量 67

猜你喜欢

转载自blog.csdn.net/qq_40285768/article/details/104597076