Floyd 算法与 Dijkstra 算法的不同
- Floyd 算法是求任意两点之间的最短路径,是多源最短路径。而Dijkstra(迪杰斯特拉)算法是求某一个顶点到其他所有顶点的最短路径,是单源最短路径。
- Floyd 算法属于动态规划,我们在写核心代码时候就是相当于推dp状态方程,Dijkstra(迪杰斯特拉)算法则属于贪心算法。
- Dijkstra(迪杰斯特拉)算法时间复杂度一般是o( n 2 n^2 n2),Floyd算法时间复杂度是o( n 3 n^3 n3),Dijkstra(迪杰斯特拉)算法比Floyd算法块。
- Floyd 算法可以计算带负权值的;而Dijkstra算法只能解决边权重非负的最短路径问题。
- Dijkstra 算法通过选定的被访问节点,求出从源节点到其他节点的最短路径;而 Floyd 算法中每一个节点都是出发节点(源节点),所以需要将每一个节点看作被访问节点,求出从每一个节点到其他节点的最短路径。
比如:Dijkstra 算法一次只能求取 A 到 B、C 的最短路径长度,即它是单源最短路径。而 Floyd 算法只需要一次就可以求出 A 到 B、C,B到A、C,C 到 A、B 所有的最短路径,即它是多源最短路径。
注:在这里我直接把对角线上的元素也都初始化为了N,其实这样有点问题,因为是多源最短路径算法,若不指定到自身的距离为零,算法将找到经由其他节点再回到自身的“最短”路径。所以对角线最好初始化为0。
如果只是求从每一个节点到其他节点的最短路径,而不求具体的最短路径是什么,那么就不需要path数组:
public class Floyd_Simple {
public static final int N = 65535;
public static void main(String[] args) {
int[][] weight = {
{N, 10, N, 30, 100},
{N, N, 50, N, N},
{N, N, N, N, 10},
{N, N, 20, N, 60},
{N, N, N, N, N}
};
floyd(weight);
}
public static void floyd(int[][] weight){
for(int i = 0; i < weight.length; i++){// i 是中间节点的下标
for(int j = 0; j < weight.length; j++){// j 是起始节点的下标
for(int k = 0; k < weight.length; k++){// k 是终点节点的下标
int len = weight[j][i] + weight[i][k];// 求出从 j 节点出发,经过 i 中间节点,到达 k 节点的距离。即 j->i->k
// 如果 j->i->k 的距离小于 j->k 的距离
if(len < weight[j][k]){
weight[j][k] = len;// 更新距离
}
}
}
}
// 输出结果
for(int i = 0; i < weight.length; i++){
for(int j = 0; j < weight.length; j++){
if(i != j){
if(weight[i][j] == N){
System.out.println(i + "到" + j + "不可达");
}else{
System.out.println(i + "->" + j + "的最短路径长度是:" + weight[i][j]);
}
}
}
}
}
}
将具体的最短路径是什么也求出来:
public class Floyd {
public static final int N = 65535;
public static void main(String[] args) {
int[][] weight = {
{N, 10, N, 30, 100},
{N, N, 50, N, N},
{N, N, N, N, 10},
{N, N, 20, N, 60},
{N, N, N, N, N}
};
// 记录路径
int[][] path = new int[weight.length][weight[0].length];
for(int i = 0; i < path.length; i++){
for(int j = 0; j < path[i].length; j++){
path[i][j] = -1;// 开始都初始化为-1
}
}
floyd(weight, path);
}
public static void floyd(int[][] weight, int[][] path){
// i 是中间节点的下标
for(int i = 0; i < weight.length; i++){
// j 是起始节点的下标
for(int j = 0; j < weight.length; j++){
// k 是终点节点的下标
for(int k = 0; k < weight.length; k++){
int len = weight[j][i] + weight[i][k];// 求出从 j 节点出发,经过 i 中间节点,到达 k 节点的距离。即 j->i->k
// 如果 j->i->k 的距离小于 j->k 的距离
if(len < weight[j][k]){
weight[j][k] = len;// 更新距离
path[j][k] = i;// 记录从 j 到 k 经由哪个点到达
}
}
}
}
for(int i = 0; i < weight.length; i++){
for(int j = 0; j < weight.length; j++){
if(i != j){
if(weight[i][j] == N){
System.out.println(i + "到" + j + "不可达");
}else{
System.out.println(i + "->" + j + "的最短路径长度是:" + weight[i][j]);
System.out.print("最短路径为:");
printPath(path, i, j);
}
}
}
}
}
public static void printPath(int[][] path, int v1, int v2){// 求出从 v1 到达 v2 的最短路径
int temp = path[v1][v2];
System.out.print(v1);
while(temp != -1){
System.out.print("->" + temp);
temp = path[temp][v2];
}
System.out.println("->" + v2);
}
}