最短路径(Dijkstra、Bellman-Ford和SPFA算法)

前言

提醒一下:最短路径一般用于有向网(就是有方向和权值的图),而最小生成树一般是用于无向网。
本来是想分开写,不过想到这样大家看起来比较麻烦。就写在一起了,这样可以很好的进行对比。

图的存储方式

常用的无非就

  1. 邻接矩阵
  2. 邻接表
  3. 链式前向星

邻接矩阵

在这里插入图片描述

这个可以说是最好理解的了,分为以下几种情况

  • 无权值得图,初始化为0,有边想连(有关系)就赋值为1
  • 有权值图(网),初始化为 INF(一个大的常数),有边想连(有关系)就赋值为 该权值

邻接表

可以说邻接表的建图就很花里胡哨了,如果不理解它的意思,肯定被每一个人都不一样的邻接表给弄糊涂的。为了让大家看下边的代码不乱,特意说明一下。

链表建立

在这里插入图片描述

  • 对于链表,就是一个一维数组,其中存放表头节点,每一个表头节点都连着一个链表,是由其相邻的顶点组成。

利用vector

基于链表中的特性,可以用一个vector数组来代替链表

  • 如果是不带权值的
vector<int>edge[N];


int main()
{
   
	int V, E;
	cin >> V >> E;
	for (int i = 0; i < E; ++i)
	{
   
		int u,v;
		cin >> u >> v;
		edge[u].push_back(v);
		/*
		如果是无向图
		edge[u].push_back(v);
		edge[v].push_back(u);
		*/
	}
	// 遍历,通过顶点来遍历,
	for (int i = 1; i <= V; ++i)
	{
   
		for (int j = 0; j < edge[i].size(); ++j)
			cout << edge[i][j] << " ";
	}

}
  • 如果有权值的话,有两个地方改就好
#define pi pair<int,int>
vector<pi>edge[N];
  • 存的时候,
for (int i = 0; i < E; ++i)
	{
   
		int u,v,w;
		cin >> u >> v>>w;
		edge[u].push_back(make_pair(v,w));
		/*
		如果是无向图
		edge[u].push_back(make_pair(v,w));
		edge[v].push_back(make_pair(u,w));
		*/
	}

结构体

结构体的话就想到与用边来作为一个头来建立


//从顶点u到顶点v的权值为w的边
struct edge
{
   
	int u, v, w;
};
edge es[N];//储存所有边

int main()
{
   
	int V, E;
	cin >> V >> E;
	for (int i = 1; i <= E; ++i)
	{
   
		int u, v,w;
		cin >> u >> v>>w;
		es[i] = edge{
    u, v, w };
		/*
		如果是无向图
		es[i] = edge{ u, v, w };
		es[i] = edge{ v, u, w };
		*/
	}
	// 遍历,通过边来遍历,
	for (int i = 1; i <= E; ++i)
	{
   
		edge e = es[i];
		cout << e.u << " " << e.v << " " << e.w << endl;
	}

}
相信说到这,就可以慢慢悟了把

核心思路

本来想放在后头,不过怕大家一路看过去直接迷了。就放在前边,可以带着这个去看.

Dijkstra算法

// dis[k] 中 dis[k] 表示源点到 顶点k的距离(直接到k)
//dis[mark] + g[mark][k]  先到当前距离短的点mark,通过这个点转到k
dis[k] = min(dis[k], dis[mark] + g[mark][k]); //取最小的距离

Bellman-Ford和SPFA算法

if (dis[e.v] > dis[e.u] + e.w)
	dis[e.v] = dis[e.u] + e.w;

其实大家理解和这两个是一个道理。都可以通过下面这段话理解。

可以想一下有三个点,1,2,3.刚开始1到3距离为6,1到2是3,2到3是2.以1为源点,因为到顶点2的距离小,所以将2加入。这个时候在更新距离时,之前没有加入2的时候1到3只有1–>3这个路径可以走,而现在2加入后,有新的路径了1–>2–>3,我们在算一下长度,哇才是5,比直接走到3小,好那么我们就改变路径。这就是上面代码的意义。

Dijkstra算法

图解

由于本博主画的图太,借了点图
在这里插入图片描述
----- S是已计算出最短路径的顶点的集合
----- U是未计算出最短路径的顶点的集合
----- C(3)表示顶点C到起点D的最短距离为3

  1. 选择顶点D
    S={D(0)}
    U={A(∞), B(∞), C(3), E(4), F(∞), G(∞)}
    在这里插入图片描述
  2. 选取顶点C
    S={D(0), C(3)}
    U={A(∞), B(13), E(4), F(9), G(∞)}
    在这里插入图片描述
    就这样当作一个引子

基本思想

把带权图中的所有顶点V分成两个集合S和T,S中存放已经确定最短路径的顶点,初始时,S中仅包含一个源点; T=V−S,存储待确定最短路径的顶点,初始时,T中包含除源点外的顶点,此时各顶点的当前最短路径长度为源点到各顶点的弧上的权值。开始操作时,从T中选取当前最短路径长度最小的一个顶点 Vi 加入S,然后修改T中剩余顶点的当前最短路径长度,修改的原是:当 Vi 的最短路径长度与 Vi 到T中的顶点之间的权值之和小于该顶点的当前路径长度时,用前者替换后者。重复上述过程,直到S中包含所有的顶点
为止。

简单来说,将点分为两个集合,将源点放入一个集合中,其余的点放入另一个集合。(实现算法不需要用到集合,可以用一个bool 数组来记录,为true就表明就加入到了源点所在的集合中)。然后遍历不在源点集合的点,找到距离源点最近的点。将这个点加入到源点所在的集合中。然后以这个点为中转点更新源点到其余点的距离。

求解步骤

  1. 将有向网用邻接矩阵储存,没有相连的边用一个较大的数表示。
  2. 找到距离源点最近的点,将其加入到源点所在的集合中。
  3. 以这个点为中转点,更新源点到其余点的距离。

细节解释

变量的意义

int g[N][N];//邻接矩阵存图,g[i][j] 表示 顶点i带顶点j的距离
int dis[N];//表示 源点src到 i的最短距离
bool vis[N];//是否在集合中
int stc;//源点
int n, m;//n 顶点数,m边数

初始化


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落春只在无意间

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值