最短路基础算法模板大全

最短路的定义可以说是很好理解了,就是一个点到另一个点的最短距离。
最短路又分为两种,单源最短路和多源汇最短路
其实定义上没有什么好理解的,这里就引入两个概念,然后。。。背模板就好了吧(我觉的,应该是这样QwQ)

单源最短路

这里先简单的理解一下,其实源也就是一个图的起点,而汇就是我们要找的终点。
这里简单理解,源是起点,汇是终点,就OK了。
单源最短路,也就是从一个点出发,到其他各各点的最短路径,主要分为两种情况,无负权值边的情况和有负权值边的情况,每一种情况也有不同的算法。

<1>无负权值边

先说无权值边的情况,主要是一种算法的两种版本,用于应对不同的情况。

1.朴素Dijkstra算法

这种算法的复杂度为O(n^2),(我们先规定,n为点的个数,m为边的个数),适用于点少边多(稠密图)的情况。

void dijkstra()
{
	memset(dist, INF, sizeof(dist));   //dist代表 1 到第 n 个点的最短路
	memset(vis, 0, sizeof(vis));  //vis标记已经确定的点
	for(int i = 1; i < n; i ++)
	{
		int t = -1;   // 用于寻找当前dist最短的点 
		for(int j = 1; j <= n; j ++)
		{
			if(!vis[j] && (t == -1 || dist[j] < dist[t]))
				t = j;
		}
		vis[t] = 1;
		for(int j = 1; j <= n; j ++)
			dist[j] = min(dist[j], dist[t] + g[t][j]) //g是邻接矩阵,
								//代表点 t 到点 j 的距离
	}
}

这里还要提一下下,对于所有的稠密图,都可用邻接矩阵进行储存。

2.堆优化的Dijkstra算法

这个算法的时间复杂度是O(m * logn),很显然,在m < n^2 的情况下,堆优化的Dijstra算法可以更快的完成计算,所以对于边较少(稀疏图)的情况,我们应该用堆优化的算法来完成最短路。

const int N = 1e5 + 8;
const int INF = 0x3f;
int h[N], e[N], ne[N];   //稀疏图,用邻接表的方式存储
int dist[N], w[N];   //w用于记录权值
bool vis[N];   //用于标记该点距离是否已经确定

typedef pair<int, int> PLL;    //因为最短路有两个数据,优先队列中用pair类型

void dijkstra()
{
	priority_queue<PLL, vector<PLL>, greater<PLL>> head;  //定义一个小根堆
	memset(dist, INF, sizeof(dist));
	dsit[1] = 0;
	head.push({0, 1});
	while(!head.empyt())
	{
		auto t = head.top();
		head.pop();
		int num = t.second, distance = t.first;
		if(vis[num]) continue;   //如果该点最短距离已经确定,直接搜下一个点
		vis[num] = true;
		for(int i = h[num]; i != -1; i = ne[i])   //查找最短距离
		{
			int j = e[i];
			if(dist[j] > w[j] + distance) 
			{
				dist[j] = w[j] + distance;
				head.push({dist[j], j});
			}
		}
	}
}

同样的,所有的稀疏图,都可以用邻接表的形式储存。

<2>有负权值边

有负权值边的情况,有两种算法,在下面也会给出基本思路和模板QwQ

1.Bellman-Ford算法

Bellman-Ford算法的时间复杂度为O(nm),通常用于有限制经过不超过多少条边的题目(因为另一种SPFA算法是这个算法的优化,没有这种情况了话用SPFA就好了QwQ)

const int N = 505, M = 100010;
const int INF = 0x3f;
int dist[N], backup[N]    //backup用于拷贝数据
int n, m, k;   //n为边的数量,m为边的数量,k为限制边数
struct Edge
{
	int a, b, w;
}edges[M];

int bellman_ford()
{
	memset(dist, INF, sizeof dist);
	dist[1] = 0;
	for(int i = 0; i < k; i ++)
	{
		memcpy(backup, dist, sizeof dist);  //拷贝数据
		for(int j = 0; j < m; j ++)
		{
			int a, b, w;
			a = edges[j].a, b = edges[j].b, w = edges[j].w;
			dist[b] = min(dist[b], backup[a] + w); 
		}
	}
	if(dist[n] > 0x3f3f3f3f / 2) return -1;  //判断是否可以到达n
	else return dist[n];
}
2.SPFA算法

SPFA算法是Bellman_Ford算法的优化,时间复杂度一般为O(m),即便最坏的情况,复杂度也只为O(nm),与Bellman_Ford算法一样,所以在不限制边数的情况下,SPFA算法基本可以解决所有的最短路问题。

const int N = 100010;
const int INF = 0x3f;
int h[N], ne[N], e[N], w[N];    //也是用邻接表储存数据
int dist[N], vis[N];

int spfa()
{
	queue<int> q;
	memset(dist, INF, sizeof dist);
	dist[1] = 0, vis[1] = 1;
	q.push(1);
	while(!q.empty())
	{
		auto t = q.front();
		q.pop();
		vis[t] = 0;  //点被拿出,标记消除
		for(int i = h[t]; i != -1; i = ne[i])
		{
			int j = e[i];
			if(dist[j] > dist[t] + w[i])
			{
				dist[j] = dist[t] + w[i];
				if(!vis[j])
				{
					q.push(j);
					vis[j] = 1;
				}
			}
		}
	}
	if(dist[n] == 0x3f3f3f3f) return -1;
	else return dist[n];
}

这个算法和堆优化的Dijkstra算法长的蛮像的,两个可以一起记呀QwQ
SPFA算法还可以用来判断是否存在负环。。。这里也写一下吧QwQ

const int N = 100010;
int h[N], e[N], ne[N], w[N];
int dist[N], cnt[N];    //cnt用来存边的数量
bool vis[N];

bool spfa()
{
	queue<int> q;  
	for(int i = 1; i <= n; i ++)   //因为不知道负环位置,所以需要存入所有点
	{
		q.push(i);
		vis[i] = true;
	}
	while(!q.empty())
	{
		auto t = q.front();
		q.pop();
		vis[t] = false;
		for(int i = h[t]; i != -1; i = ne[i])
		{
			int j = e[i];
			if(dist[j] > dist[t] + w[i])
			{
				dist[j] = dist[t] + w[i];
				cnt[j] = cnt[j] + 1;  //如果存在,边数加一
				if(cnt[j] >= n) return true;  //边数位n时,存在n + 1个点,则一定存在负环
				if(!vis[j])
				{
					q.push(j);
					vis[j] = true;
				}
			}
		}
	}
	return false;
}

多源汇最短路

多源汇最短路只有一个算法,还蛮短的,嘿嘿,直接记吧QwQ

Floyd算法

Floyd算法可以说是最短最好记的最短路算法了QwQ
唯一的缺点就是时间复杂度是O(n^3),可以说是非常的高了。。。
所以,有事没事别乱用QwQ

const int N = 205, INF = 0x3f3f3f;
int d[N][N];
int n, m;

void flody()
{
	for(int k = 1; k <= n; k ++)
		for(int i = 1; i <= n; i ++)
			for(int j = 1; j <= n; j ++)
				d[i][j] = min(d[i][j], d[i][k] + d[k][j]); 
		//用到了dp的思想
}

然后就是介个算法的初始化又一点点不同,我在下面写一写QwQ

for(int i = 1; i <+ n; i ++)
	for(int j = 1; j <= n; j ++)
	{
		if(i == j) d[i][j] = 0;
		else d[i][j] = INF;
	}

好了,这些就是最短路了,背完模板就会了,真的一点都不难(才怪!!!QwQ)
终于写完了,嗷呜呜呜呜~~(菜鸡哀嚎)

你可能感兴趣的:(最短路基础算法模板大全)