图论-Dijkstra算法-描述+证明+C语言代码

本文深入讲解Dijkstra算法,一种在正权有向图中寻找源点到其他点最短路径的经典算法。通过生动的比喻和详细的步骤说明,帮助读者理解算法的核心思想与实现过程。同时,提供了C语言实现的示例代码,演示如何解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Dijkstra算法
在正权有向图寻找源点到其它点的最短路径

一、实现:
定义: “最短路径值”:某个点到源点的最短距离

核心思想:
每新知道某个点的最短路径值,都更新其邻接点的的未知最短路径值(根据邻接边权值)。
将剩下点中未知最短路径值最小的那个点,确定为已知点,其未知最短路径值确定为那个点的最短路径值(即知道了一个新点的最短路径值)。继续更新,继续确定,直到确定了所有点的最短路径值(到源点的最短距离)。

可以想象一个画面:
黑暗的平面上,只有一个源点独自闪光,
其它点都隐藏在虚空之中,与源点毫无瓜葛。

以源点为中心扩散开金色的波动,其它点到源点的距离被更新了,
离源点最近的点从黑暗中现身,也扩散开金色的波动,其它点到源点的距离又被更新了
又一个离源点最近的点从黑暗中现身,又扩散开金色的波动。。。

就这样,所有的点都被激活了,如果被激活的点还记得前世那个点燃它的点,并与它连上一条金色的丝线,
那么,所有的最短路径都浮现在你的眼前,所有的回忆都熠熠闪光,清晰可见
值得注意的是,被唤醒的点不一定是前一个被唤醒点的后继,而可能早有注定之人(更早的唤醒点更新了其到源点的最短路径)。

具体化:
//定义
“最短路径值 Lk”:k点到源点s的最小距离
wij:从i点到j点的有向边的权值
S集:已知最短路径值点的集合
Q集:未知最短路径值点的集合

//初始化
初始化源点s的最短路径值(到自身)为0,其它点的最短路径值为∞,
S集为空集,即没有已知最短路径值
Q集为点集,即所有点都未知最短路径值
//从Q集(未知最短路径点)中选点加入S集(已知最短路径点)
找出Q集中最短路径值最小的点v,放入S集,称v点已知最短路径值
第一次,点v为源点s,Ls=0,L其它=∞

//更新Q集未知最短路径点的最短路径值
以此更新去掉v点后Q集中v点的邻接点q的最短路径值
Lq=min(Lq,Lv+wvq),
即,如果以新选出的已知最短路径点v的最短路径作为源点走到q的前路,能使q的最短路径值变小,就替换掉原来的最短路径值。
第一次,v点为源点s,L其它被更新,L邻接s=邻接边权值,L不邻接s=∞

//重复 选出已知最短路径值的点,更新未知最短路径值点的最短路径值
也就是每次都贪心的确定最小未知最短路径值,作为已知的最短路径值

//如果要记录路径
被更新成功的点记录更新它的桥梁点,一直找桥梁点可以找回源点,被找到的点,就是最短路径。

二、数学证明:
终于见到了一篇令我茅塞顿开的文章:
https://www.cnblogs.com/star-eternal/p/7704532.html

参考链接文章所写的数学证明:
证明按照该算法确定的已知最短路径点,确实是最短路径点。
①n=1
该算法确定的第一个点为源点s,确实是最短路径点。
②n=2
该算法确定的第二个点为源点s最近的一个邻接点,确实是最短路径点。
③假设n=k成立,证明n=k+1成立
即证明,当按照该算法找到的k个点,其Lk确实为最短路径值时,按照该算法找到的第k+1个点q,设其桥梁点是p,它的最短路径值L就是Lq=Lp+wpq。
反证:
假设q点有更小的最短距离值L,使L<Lq=Lp+wpq,
因为大前提已经找到的k个点,其Lk确实为最短路径值,且这些最短路径都是在已知最短路径的基础上延伸出来的,根据所有已知最短路径点更新未知最短路径点的结果,找出L=Lp+wpq = Lq,故经过已知最短路径点到q点,其L = Lq
故实际最短路径L,必经过一未知最短路径点x,才有可能使L<Lq。
此时L=Lx+wxq。由于L>Lx,且x也是未知最短路径点,故按照算法找到的第k+1个点不是q而是x了,与大前提 按照算法找到的第k+1个点为q矛盾。
故q点没有更小的最短距离值,即按照算法找出的Lq=Lp+wpq就是q点的最短距离值。

综上:该算法找到的最短路径点的最短路径值,确实是最短路径值。贪心有理。

三、C语言实现的程序

// 编写软件实现下面功能
// 1. 输入城市个数n,建立n个城市铁路关系及费用 
// 2. 求任意2个城市的最小开销并输出路径 

#include <stdio.h>
#include <stdlib.h>

#define N 100		//数组最大数目 
#define INF 10000 // 距离无穷大,无路

void Dijkstra(int num, int from, int to, double edge[N][N]) //点数,源点,目标点,边关系二维数组 
{
	double d[N];	//d[i]存储 i点到源点的最短距离 
	int finded[N];   //finded[i]=1表示已找到i点最短距离,即纳入基本点集
	int path[N];	//保存前一个点 

	//初始化
	int i;
	for (i = 1; i <= num; i++)
	{
		d[i] = 10000;	 //最短距离目前全为 直接到源点的距离 
		finded[i] = 0;   //没有基本点
		path[i] = from;  //所有点的上一个点为源点	
	}
	d[from] = 0;

//主循环,循环城市个数次 
	for (i = 1; i <= num; i++)
	{
		//纳入新的基本点
		double min = INF;
		int j;
		int point;//新的基本点
		//找到新的基本点
		for (j = 1; j <= num; j++)
		{
			if (!finded[j] && d[j] < min) //如果不是基本点,且到源点的最短距离最小	
			{
				min = d[j];
				point = j;
			}
		}
		finded[point] = 1;//纳入新的基本点 

		//更新非基本点的最短距离
		int k;
		for (k = 1; k <= num; k++)
		{
			if (!finded[k] && (d[point] + edge[point][k] < d[k])) //如果不是基本点,以新基本点为桥梁点看是否要更新最短距离, 
			{
				d[k] = d[point] + edge[point][k]; //该点最短距离更新 
				path[k] = point;	//该点前一个点更新 
			}
		}
	}

	if (d[to] >= INF)
	{
		printf("\n这两个城市之间不存在通路!查找失败\n");
		return;
	}

	printf("\n这两个城市之间的最短距离为:%.2lf\n", d[to]);
	printf("最短路径为:");

	printf("%d<-", to);
	int p = to;
	while (from != path[p])
	{
		p = path[p];
		printf("%d<-", p);
	}
	printf("%d\n", from);
}

int main()
{
	int Cnumber, from, to; //城市数,源点,目标点

	printf("请输入城市个数:");
	scanf("%d", &Cnumber);

	double edge[N][N];//边关系二维数组 
	//输入边关系二维数组 
	int i, j;
	for (i = 1; i <= Cnumber; i++)		//初始化城市之间的费用 
		for (j = 1; j <= Cnumber; j++)
		{
			if (i == j)
			{
				edge[i][j] = 0;
			}
			else
			{
				printf("请输入第%d个城市到第%d个城市的费用,(若两个城市之间没有关联,请输入大于等于10000的数):\n", i, j);
				scanf("%lf", &edge[i][j]);
			}
		}

	printf("请输入您想要查询的两城市编号(使用空格隔开):");
	scanf("%d %d", &from, &to);

	Dijkstra(Cnumber, from, to, edge);

	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值