Dijkstra
BFS可以用来求无权图的最短路径,Dijkstra是单源最短路径算法,一般的DIJ算法不能用于含负权边的图,本质上还是贪心思想:
- 将所有结点分为两类:已经确定最短路径的点集\(u\)、未确定最短路径的点\(v\);
- 初始化\(dis[start]=0\),其余结点\(dis\)设为无穷大;
- 找出一个\(dis\)最小的\(v\)中的点\(x\),将其标记为\(u\),遍历\(x\)的所有邻接点\(y\),若\(dis[y]>dis[x]+w[x][y]\),则更新\(y\)点到源点的最短路径长度\(dis[y]=dis[x]+w[x][y]\);
- 重复,直到所有点都在\(u\)中。
邻接矩阵版,适合顶点比较少的图,复杂度\(O(2V^2)\)
邻接表版,复杂度\(O(V^2+E)\)
寻找点\(x\)的循环可以用堆来优化,使得复杂度降为\(O(VlogV+E)\)。
堆的优化要注意2点:
- 小根堆要按照\(dis\)组织,但是在调整时要保证\(dis\)到顶点索引的映射关系,所以堆存储\((index,dis)\)
- 松弛时堆里已经有某些顶点,这些顶点更新后的新值也要压入堆,原先堆里的顶点既无法更新,也无法删除。所以我们允许堆里出现相同的顶点,但是对于
marked
标记过的点直接pop+continue
即可。
C++实现在这里
1 | // 邻接矩阵 |
1 | def dijkstra(g, s): |
Bellman-Ford
可以计算含有负权的边,检测是否存在一个从源结点可以到达的权重为负值的环路(负权环):
1 | bool bellmanFord(vector<vector<int>>& edge, vector<int>& dis, int start) { |
复杂度\(O(VE)\)。
Floyd-Warshall
多源最短路径算法。
不能用于有累加和为负的环的图,这种图不存在最短路径。1
2
3
4
5
6
7
8
9void floyd(vector<vector<int>>& m) {
for(size_t k = 0;k < m.size();++k)
for(size_t i = 0;i < m.size();++i)
for (size_t j = 0; j < m.size(); ++j) {
// m不能用INT_MAX初始化,会溢出,用0x3f3f3f3f
if (m[i][k] + m[k][j] < m[i][j])
m[i][j] = m[i][k] + m[k][j];
}
}