Dijkstra算法及证明

本文深入解析Dijkstra算法的工作原理及其在寻找图中两点间最短路径的应用,通过逐步分解算法步骤,辅以实例代码,帮助读者理解并掌握该算法。同时,文章提供了算法的时间复杂度分析和正确性证明,确保读者能够全面掌握Dijkstra算法。

Dijkstra算法及证明

问题描述

有n个点,m条边,求长度为n的数组dis,其中dis[i]表示从源点s到点i的最短距离

复杂度

O(mlogn)O(mlogn)O(mlogn)

算法步骤

  1. 令dis[s] = 0,其余节点的dis为无穷大,并将节点s入队
  2. 找出一个未被标记的,dis[x]最小的节点x,然后标记节点x
  3. 遍历x的所有相邻边(x,y,z),若dis[y] > dis[x] + z,则使用dis[x]+z更新dis[y]并将节点y入队
  4. 重复上述2 ~ 3步,直到所有节点都被标记

算法证明

首先令被标记的点集为S,其余为T

每次从队列中拿出一个dis值最小的点,如果这个点未被标记就将其标记,这样可以保证源点s一定是经由S集中的点到达它,因为每次都是用S集中的点来更新到其他点的距离

其次,当一个点x从队列中拿出并将其标记时,它当前的dis值一定是最短距离,从以下两个方面证明:

  1. 如果到点x的最短路经过了集合T中的点,假设这条最短路的路径为s -> i1 -> i2 -> … -> y -> … -> x,其中y为这条路径上第一个T集合中的点,那么必然有dis[y] < dis[x],这样按照算法的第2步应该拿出来的是y号点,故到x点的最短路径中的所有点一定是在集合S中
  2. 由以上证明可知当前的dis[x]一定是经由S中的点到达的,假设点x是第k个加入集合S中的点,易知加入集合中的第一个点s的dis[x]是最短路径,由数学归纳法假设前k-1个点加入到集合中的时候dis值都是最短路径,因为加入集合时它们都更新了与之相邻的点的dis值,那么当拿出第k个点时dis值必然是最短距离,得证

(以上证明若有不严谨之处欢迎指出)
补充:想了想这不就是带优先队列的BFS吗,似乎根本不用证

例题

https://vjudge.net/problem/10951/origin

题意:有n个点m条边,问从1号点到n号点的最短距离

代码:

#include<bits/stdc++.h>
#define MAXN 105
#define MAXM 10005
#define INf 0x3f3f3f3f
using namespace std;
int n,m,tot,head[MAXN];
struct edge
{
	int v,w,nxt;
}edg[MAXM<<1];
inline void addedg(int u,int v,int w)
{
	edg[tot].v = v;
	edg[tot].w = w;
	edg[tot].nxt = head[u];
	head[u] = tot++;
}
int dis[MAXN];
struct node
{
	int id,w;
	node(){}
	node(int id,int w):id(id),w(w){}
	friend bool operator < (node n1,node n2)
	{
		return n1.w < n2.w;
	}
}nod;
priority_queue<node> qu;
inline void dijkstra(int s)
{
	memset(dis,0x3f,sizeof(dis));
	dis[s] = 0;
	while(!qu.empty())
		qu.pop();
	qu.push(node(s,0));
	int u,v,w;
	while(!qu.empty())
	{
		nod = qu.top();
		qu.pop();
		u = nod.id,w = nod.w;
		if(w != dis[u])
			continue;
		for(int i = head[u];i != -1;i = edg[i].nxt)
		{
			v = edg[i].v,w = edg[i].w;
			if(dis[v] > dis[u] + w)
			{
				dis[v] = dis[u]+w;
				qu.push(node(v,dis[v]));
			}
		}
	}
}
inline void init()
{
	tot = 0;
	memset(head,-1,sizeof(int)*(n+1));
}
int main()
{
	while(~scanf("%d%d",&n,&m) && n && m)
	{
		init();
		int u,v,w;
		while(m--)
		{
			scanf("%d%d%d",&u,&v,&w);
			addedg(u,v,w);
			addedg(v,u,w);
		}
		dijkstra(1);
		printf("%d\n",dis[n]);
	}
	return 0;
}
Dijkstra算法是一种用于在图中找到最短路径的算法,特别是用于带权重的有向图,其中权重不一定是负数。该算法由荷兰计算机科学家Edsger W. Dijkstra在1956年提出,并在1959年发表。其基本思想是贪心算法算法过程如下: 1. 创建两个集合:已访问顶点集合和未访问顶点集合。 2. 将起始顶点加入已访问集合,并初始化所有其他顶点的距离为无穷大。 3. 选择未访问集合中距离起始顶点最近的顶点,将其加入已访问集合。 4. 更新当前顶点的邻居节点的距离值,如果通过当前顶点到达邻居的距离小于已知的最短距离,则更新该距离。 5. 重复步骤3和4,直到所有顶点都被访问。 算法证明通常基于数学归纳法。简要证明如下: 假设图中有一个顶点集合V和一个源点s。我们要证明算法的每一步中,对于任意顶点v,算法计算出来的距离dist[v]是从源点s到v的最短路径长度。按照Dijkstra算法的步骤,每次从未访问的顶点中选取距离源点最近的顶点u,并将其加入已访问集合。 数学归纳法的归纳步骤如下: - 基础情况:算法开始,只确定了源点s的距离为0,这是正确的,因为源点到自身的距离为0。 - 归纳假设:假设在算法的第k次迭代后,对于所有已访问的顶点u,dist[u]是从源点s到u的最短路径长度。 - 归纳步骤:现在我们要证明算法的第k+1次迭代后,对于所有已访问的顶点v,dist[v]也是正确的最短路径长度。考虑新增加到已访问集合中的顶点w,我们有dist[w]是从源点s到w的最短路径长度。因为在选择顶点w算法已经考虑了所有可能的路径,并且确保了w是从s到未访问顶点中距离最短的。接着更新所有未访问的邻居节点的距离,如果存在更短的路径,算法更新这些顶点的dist值。因此,归纳假设对于算法的第k+1次迭代也是成立的。 通过归纳法,我们可以得出结论,对于图中的每一个顶点v,在算法结束,dist[v]是从源点s到v的最短路径长度。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值