1. Floyd
思想:
尝试将每个点添加到两点间, 看距离是否变短, 变短则更新
输入: 图矩阵
输出: 每个点到每个点的最短距离
核心代码:
for(k = 1; k <= n; k++)
for(j = 1; j <= n; j++)
for(i = 1; i <= n; i++)
if(e[i][j] > e[i][k] + e[k][j])
e[i][j] = e[i][k] + e[k][j] //松弛操作
2. Dijkstra(单源点最短路径)
思想
找到未知的离源点最短距离的点 , 尝试将该点加入到两点之间, 看距离是否变短
这里将 Floyd 的每个点改成了 离源点最短距离的点 了
- 这里因为每次都只考虑未知的点, 所以当有负权边出现, 而在此之前已经确定了某几个点时, 有可能经过负权边之后, 以前确定的最短距离会更短, 所以不可解决负权边.
输入: 矩阵图
输出: 源点到每个点的最短路径
数据结构:
dis: 存储该点到其他点的最短距离
e : 无向图
算法
循环 n 次以下步骤:
-
找到 dis 未访问过且最短距离的点: u u u
-
是否找到该点:
否: 退出循环
-
将该点状态置为已访问过, 标记为已知最短距离 visit[u] = true
-
尝试把该点 v v v 添加到两个点(u 与 源点)的路径之间 e v , u + d i s u e_{v,u} + dis_u ev,u+disu
-
添加后距离是否减小 d i s v > e v , u + d i s u dis_v > e_{v,u} + dis_u disv>ev,u+disu
是: 添加
否:不添加
-
核心代码
for (int i = 0; i < n; i++){
int u = -1; min = inf;
for(int j = 0; j < n; j++){
if(visite[j] == false && dis[j] < min){
min = dis[j];
u = j;
}
}
if(u == -1) break;
visit[u] = true;
for(int v = 0; v < n; v++){
if(visit[v] == false && e[u][v] != inf){
// next,your code depends on needs
if(dis[v] > e[u][v] + dis[u]){ //松弛操作
dis[v] = e[u][v] + dis[u];
}
}
}
}
3. Bellman-Ford(单源点最短路径, 不受负权边影响)
思想
执行 n-1次以下操作: 遍历所有的边, 尝试把每一条边添加到两点之间
- 因为每次都会考虑所有边, 所以这里不会忽略负权边, 解决了负权边问题
输入 : v[n]; u[n]; w[n], 分别表示: 一条边的两个顶点, 边的权重
输出: dis, 源点到其他点的最短路径
核心代码:
for(k = 0; k <= n; k++)
for(i = 0; i < m; i++)
if(dis[v[i]] > dis[u[i]] + w[i])
dis[v[i]] = dis[u[i]] + w[i];
4. 路径记录:
在松弛操作的时候, 记录该点前一个点即可:
如 Dijkstra 松弛部分:
vector<int> path[n]
//....sth to do....
for(int v = 0; v < n; v++){
// 松弛部分
if(visit[v] == false && e[u][v] != inf){
// next,your code depends on needs
if(dis[v] > e[u][v] + dis[u]){ //松弛操作
dis[v] = e[u][v] + dis[u];
// 记录前一条
path[v].clear();
path[v].push_back(u);
}else if(dis[v] == e[u][v] + dis[u]){ //有多条路径时
path[v].push_back(u);
}
}
}