- 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;
}