https://zhuanlan.zhihu.com/p/33162490
Floyd
一种基于动态规划的多源最短路算法
private void floyd() {
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
a[i][j] = Math.min(a[i][j], a[i][k] + a[k][j]);
}
}
}
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
System.out.println(i + " " + j + ":" + a[i][j]);
}
}
}
状态转移方程:f[k][i][j] = min(f[k-1][i][k],f[k-1][i][k]+f[k-1][k][j]),然后用滚动数组优化
空间复杂度O(n^2),时间复杂度O(n^3)
Dijkstra算法
一种经典的基于贪心的单源最短路算法,其要求图中的边全部非负
public void dijkstra(int p) {
int[] d = new int[n];
Set<Integer> set = new HashSet<>(n);
set.add(p);
for (int i = 0; i < n; i++) {
d[i] = a[p][i];
}
while (set.size() < n) {
int le = Integer.MAX_VALUE;
int num = 0;
for (int i = 0; i < n; i++) {
if (!set.contains(i) && le > d[i]) {
le = d[i];
num = i;
}
}
for (int i = 0; i < n; i++) {
if (!set.contains(i)) {
d[i] = Math.min(d[i], d[num] + a[num][i]);
}
}
set.add(num);
}
for (int i = 0; i < n; i++) {
System.out.println("点" + p + "到点" + i + "的距离为:" + d[i]);
}
}
算法的时间复杂度为O(n^2),可以用优先队列优化,优化后时间复杂度为O((E+n)lgn)
该优化适合于稀疏图,如果是稠密图极端情况E = n*(n-1)/2,这时候时间复杂度就退化为O(n^2logn)了,的确是得不偿失。
在稠密图中,我们可以使用另一种优先队列使其优化到O(nlgn+V):斐波那契堆
Bellman-Ford
这个算法是最常规下的单源最短路问题,对边的情况没有要求,不仅可以处理负权边,还能处理负环
public boolean bellmanFord(int s){
int[] d = new int[n];
for(int i=0;i<n;i++){
d[i] = a[s][i];
}
for(int i=0;i<n-1;i++){
for(int j=0;j<n;j++){
for(int k=0;k<n;k++){
if(a[j][k]!=Integer.MAX_VALUE){
d[k] = Math.min(d[k],d[j]+a[j][k]);
}
}
}
}
for(int j=0;j<n;j++) {
for (int k = 0; k < n; k++) {
if (a[j][k] != Integer.MAX_VALUE) {
if(d[k]>d[j]+a[j][k]){
return false;
}
}
}
}
负环的判断:如果有源点可达的负环,说明源点到某点的最短路径不是简单路径(不断走负环达到负无穷),因此在第|V|次松弛的时候,还能被松弛,因此在第|V|次松弛的时候,还能被松弛,就说明有负环存在。
时间复杂度为O(ne),e为图的边数,在图为稠密图的时候,是不可接受的。
SPFA
第一次松弛,我们只需要将这些边松弛一下即可。第二条边必定是第一次松弛的时候的邻接点与这些邻接点的邻接点相连的边