Floyd最短路、Dijkstra最短路、SPFA最短路模板

  • Floyd最短路
    这种方法我们可以求出任意两个点之间最短路径,它的时间复杂度是O(n ^ 3)。
    核心代码
void Floyd()
{
	for (int k = 1; k <= n; k++)
	{
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				f[i][j] = min(f[i][k] + f[k][j], f[i][j]); 
			}
		}
	}
} 
  • Dijkstra最短路
    Dijkstra本质上是一种贪心,每次找的是离起点最近的,并且是确定的(不会再更新),如果有负边权,这个时候找到的就不一定是最近的了,所以它不能处理负权边。它的时间复杂度是O(n ^ 2)。
    核心代码:
    ①邻接矩阵法
void Dijkstra()
{
	for (int i = 1; i <= n - 1; i++)
	{
		int MIN = 0x3f3f3f3f; 
		for (int j = 1; j <= n; j++)
		{
			if (!vis[j] && dis[j] < MIN)
			{
				MIN = dis[j];
				cur = j;
			}
		}
		vis[u] = 1;
		for (int v = 1; v <= n; v++)
		{
			dis[v] = min(dis[v], dis[cur] + edge[cur][v]);
		}
	}
}

② 前向星存储法(推荐)

void Dijlstra()
{
	int cur = 1;
	dis[cur] = 0;
	while (!vis[cur])
	{
		vis[cur] = 1;
		for (int i = head[cur]; i; i = edge[i].next)
		{
			if (!vis[edge[i].to] && dis[edge[i].to] > dis[cur] + edge[i].w)
			{
				dis[edge[i].to] = dis[cur] + edge[i].w;
			}
		} 
		int MIN = 0x3f3f3f3f;
		for (int i = 1; i <= n; i++)
		{
			if (!vis[i] && dis[i] < MIN)
			{
				MIN = dis[i];	
				cur = i;
			}
		}
	}
}

③ 堆优化后

struct node
{
	int w, id;
	node(int _id, int _w)
	{
		id = _id;
		w = _w;
	}
	bool operator < (const node &x) const
	{
		return x > x.w;
	}
}
void Dijkstra()
{
	for (int i = 1; i <= n; i++)
	{
		dis[i] = 0x3f3f3f3f;
	}
	priority_queue<node> q;
	q.push(node(s, 0));
	dis[s] = 0;
	while (!q.empty())
	{
		int cur = q.top().id;
		q.pop();
		if (vis[cur]) continue;
		vis[cur] = 1;
		for (int i = head[cur]; i; i = edge[i].next)
		{
			if (!vis[edge[i].to] && dis[edge[i].to] > dis[cur] + edge[i].w)
			{
				dis[edge[i].to] = dis[cur] + edge[i].w;
				q.push(node(dis[edge[i].to], edge[i].to));
			} 
		}
	}
}
  • SPFA最短路(bellman-ford)
    SPFA是bellman-ford的改进算法(队列实现),效率也更高。相比于Dijkstra,SPFA可以计算带负环的回路。

SPFA 的实现如下:用数组dis记录更新后的状态,cnt 记录更新的次数,队列q记录更新过的顶点,算法依次从 q 中取出待更新的顶点 v。需要注意的是,一旦发现顶点 k 有cnt[k] > n,说明有一个从顶点K出发的负权圈,此时没有最短路,应终止算法。否则,队列为空的时候,算法得到G的各顶点的最短路径长度。
核心代码:
① 邻接矩阵法

void SPFA(int start)
{
	for (int i = 1; i <= n; i++)
	{
		dis[i] = 0x3f3f3f3f;
	}
	vis[start] = 1;
	dis[start] = 0;
	
	queue<int> q;
	q.push(start);
	while (!q.empty())
	{
		int cur = q.front();
		q.pop();
		vis[cur] = 0;
		for (int i = 1; i <= n; i++)
		{
			if (dis[cur] + edge[cur][i] < dis[i])
			{
				dis[i] = edge[cur][i] + dis[cur];
				if (!vis[i])
				{
					vis[i] = 1;
					q.push(i);
				}
			}
		} 
	}
}

② 前向星法

void SPFA(int start)
{
	for (int i = 1; i<= n; i++)
	{
		dis[i] = 0x3f3f3f3f;
	}
	queue<int> q;
	q.push(start);
	vis[start] = q;
	dis[1] = 0;
	while (!q.empty())
	{
		int cur = q.front();
		q.pop();
		vis[cur] = 0;
		for (int i = head[cur]; i; i = edge[i].next)
		{
			if (dis[edge[i].to] > dis[cur] + edge[i].w)
			{
				dis[edge[i].to] = dis[cur] + edge[i].w;
				if (!vis[edge[i].to])
				{
					vis[edge[i].to] = 1;
					q,push(edge[i].to);
				}
			}
		}
	}
}

③ 判断负圈

bool SPFA(int start)
{
	for (int i = 1; i<= n; i++)
	{
		dis[i] = 0x3f3f3f3f;
	}
	queue<int> q;
	q.push(start);
	vis[start] = q;
	dis[start] = 0;
	cnt[start]++;
	q.push(start);
	while (!q.empty())
	{
		int cur = q.front();
		q.pop();
		vis[cur] = 0;
		for (int i = head[cur]; i; i = edge[i].next)
		{
			if (dis[edge[i].to] > dis[cur] + edge[i].w)
			{
				dis[edge[i].to] = dis[cur] + edge[i].w;
				cnt[edge[i].to]++;
				if (cnt[edge[i].to] > n)
				{
					return  false;
				}
				if (!vis[edge[i].to])
				{
					vis[edge[i].to] = 1;
					q,push(edge[i].to);
				}
			}
		}
	}
	return true;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值