1,Floyd-Warshall算法(只有五行)
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(e[i][j]>e[i][k] + e[k][j])
e[i][j]=e[i][k]+e[k][j];
}
}
}
基本思想: 允许经过1-n号 所有顶点进行中转,求任意两点之间的最短路程,e[i][j]表示从 i顶点到 j顶点的路程。
需要注意的是: Floyd算法不能处理带有“负权回路”的图,即二维矩阵中的值不能为负数。
package 最短路径;
import java.util.*;
public class Floyd {
public static void main(String[] args){
int[][] e=new int[10][10];
int k,i,j,n,m,t1,t2,t3;
int min=99999999;
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
//初始化
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
if(i==j)e[i][j]=0;
else
e[i][j]=min;
}
}
//读入边
for(i=1;i<=m;i++){
t1=sc.nextInt();
t2=sc.nextInt();
t3=sc.nextInt();
e[t1][t2]=t3;//有向图
}
//Floyd-Warshall算法核心语句
for(k=1;k<=n;k++){
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
if(e[i][j]>e[i][k]+e[k][j])
e[i][j]=e[i][k]+e[k][j];
}
}
}
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
System.out.printf("%10d",e[i][j]);
}
System.out.println();
}
}
}
4 8
1 2 2
1 3 6
1 4 4
2 3 3
3 1 7
3 4 1
4 1 5
4 3 12
输出:
0 2 5 4
9 0 3 4
6 8 0 1
5 7 10 0
2,Dijkstra算法-单源最短路
使用二维数组e来存储顶点之间边的关系
还需要用一个一维数组dis来存储1 号顶点到其余各个顶点的初始路程。
Dijkstra主要思想:通过“边”来松弛1 号顶点到其余各个顶点的路程。
松弛的过程:如: 要求顶点1 到顶点4 的最短路程,通过2->4(e[2][4]),可以将dis[4]的值从无穷松弛为4(dis[4]初始为无穷, dis[2]+e[2][4]=1+3=4, 由于 dis[4]>dis[2]+e[2][4],因此dis[4]更新为4)。
算法的基本思想是:每次找到离源点最近的一个顶点,然后以该顶点为中心进行扩展,最终得到源点到其余所有点的最短路径。
package 最短路径;
import java.util.*;
public class Dijkstra {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[][] e=new int[10][10];
int[] dis=new int[10];
int[] book=new int[10];
int i,j,m,n,t1,t2,t3,min;
int u = 0,v=0;
int inf=99999999;
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
//初始化
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
if(i==j)e[i][j]=0;
else
e[i][j]=inf;
}
}
//读入边
for(i=1;i<=m;i++){
t1=sc.nextInt();
t2=sc.nextInt();
t3=sc.nextInt();
e[t1][t2]=t3;//有向图
}
//初始化dis数组,顶点1 到其余各顶点的路程
for(i=1;i<=n;i++){
dis[i]=e[1][i];
}
book[1]=1;
//Dijkstra算法核心语句
for(i=1;i<=n-1;i++){
min=inf;
for(j=1;j<=n;j++){
if(book[j]==0 && dis[j]<min){
min=dis[j];
u=j;
}
}
book[u]=1;
for(v=1;v<=n;v++){
if(e[u][v]<inf){
if(dis[v]>dis[u]+e[u][v]){
dis[v]=dis[u]+e[u][v];
}
}
}
}
for(i=1;i<=n;i++){
System.out.printf("%d ",dis[i]);
System.out.printf(" ");
}
}
}
6 9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
输出:
0 1 8 4 13 17
3,Bellman-Ford–解决负权边
for(k=1;k<=n-1;k++){
for(i=1;i<=m;i++){
if(dis[v[i]]> dis[u[i]]+w[i])
dis[v[i]]=dis[u[i]]+w[i];
}
}
外循环一共循环了n-1次(n为顶点的个数), m为边的个数,
dis数组和Dijkstra算法一样,用来记录源点到其余各个顶点的最短路径。u、v、w三个数组是用来记录边的信息, 例如第i条边的信息存储在u[i] v[i] w[i]中,表示从顶点 u[i] 到顶点 v[i] 这条边(u[i]->v[i])的权值为w[i]。
if(dis[v[i]]> dis[u[i]]+w[i])
dis[v[i]]=dis[u[i]]+w[i];
这两行代码的意思是: 看看能否通过 u[i]->v[i](权值为w[i])这条边,使得1 号顶点到 v[i]号顶点的距离变短。 即1 号顶点到u[i]号顶点的距离(dis[u[i]])加上 u[i]->v[i]这条边(权值为w[i])的值是否会比原先 1 号顶点到v[i]号顶点的距离 dis[v[i]]要小。
Bellman-Ford算法: 对所有的边进行n-1次 “松弛”操作,
for(k=1;k<=n-1;k++){//进行n-1次松弛
for(i=1;i<=m;i++){//枚举每一条边
if(dis[v[i]]> dis[u[i]]+w[i])//尝试对每一条边进行松弛
dis[v[i]]=dis[u[i]]+w[i];
}
}
完整代码
package 最短路径;
import java.util.*;
public class BelllmanFord {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] dis=new int[10];
int i,k,n,m;
int[] u=new int[10];
int[] v=new int[10];
int[] w=new int[10];
int inf=99999999;
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
//读入边
for(i=1;i<=m;i++){
u[i]=sc.nextInt();
v[i]=sc.nextInt();
w[i]=sc.nextInt();
}
//初始化dis数组,这里是1 号顶点到其余各个顶点的初始路程
for(i=1;i<=n;i++){
dis[i]=inf;
}
dis[1]=0;
//Bellman-Ford核心算法
for(k=1;k<=n-1;k++){
for(i=1;i<=m;i++){
if(dis[v[i]] > dis[u[i]]+w[i]){
dis[v[i]]=dis[u[i]]+w[i];
}
}
}
for(i=1;i<=n;i++){
System.out.printf("%d", dis[i]);
System.out.printf(" ");
}
}
}
5 5
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
输出:
0 -3 -1 2 4