欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java算法导论之FloydWarshall算法实现代码

程序员文章站 2024-02-23 13:54:10
摘要: 算法导论之floydwarshall算法 求一个图中任意两点之间的最短路径   floydwarshall算法是通过动态...

摘要: 算法导论之floydwarshall算法

求一个图中任意两点之间的最短路径  

    floydwarshall算法是通过动态规划来计算任意两点之间的最短路径 

        如果普通求最短路径,可以对图进行v次(顶点数)bellmanford算法。 这样的话时间复杂度为ev^2
        如果是稀疏图,则近似于v^3
        但是如果是密集图,则时间复杂度会近似达到v^4,这种情况需要优化,这里floydwarshall通过动态规划进行优化
        ,并且使用邻接矩阵来表示图。

实例代码:

package org.loda.graph;

import java.math.bigdecimal;
import java.math.roundingmode;

import org.loda.util.in;

/**
 * 
 * @classname: floydwarshall
 * @description: 求一个图中任意两点之间的最短路径
 * 
 *        floydwarshall算法是通过动态规划来计算任意两点之间的最短路径
 * 
 *        如果普通求最短路径,可以对图进行v次(顶点数)bellmanford算法。 这样的话时间复杂度为ev^2
 *        如果是稀疏图,则近似于v^3
 *        但是如果是密集图,则时间复杂度会近似达到v^4,这种情况需要优化,这里floydwarshall通过动态规划进行优化
 *        ,并且使用邻接矩阵来表示图。 
 *         d(i,j); if m=0 
 *        d(i,j,m)={
 *         min(d(i,m,m-1)+d(m,j,m-1),d(i,j,m-1)); if m!=0
 * @author minjun
 * @date 2015年6月1日 上午9:39:42
 * 
 */
public class floydwarshall {

 private double[][] d;

 private int[][] prev;

 private int v;

 private boolean negativecycle;

 public floydwarshall(int v) {
 this.v = v;

 d = new double[v][v];

 prev = new int[v][v];

 // 默认设置所有节点都不可达,而自己到自己是可达并且距离为0.0
 for (int i = 0; i < v; i++) {
  for (int j = 0; j < v; j++) {
  d[i][j] = double.positive_infinity;
  prev[i][j] = -1;
  if(i==j){
   d[i][j] = 0;
  }
  }
 }
 }

 /**
 * 
 * @title: findshortestpath
 * @description: 查询最短路径
 * @param 设定文件
 * @return void 返回类型
 * @throws
 */
 public void findshortestpath() {
 //查找最短路径
 for (int k = 0; k < v; k++) {
  //将每个k值考虑成i->j路径中的一个中间点
  for (int i = 0; i < v; i++) {
  for (int j = 0; j < v; j++) {
   //如果存在使得权重和更小的中间值k,就更新最短路径为经过k的路径
   if (d[i][j] > d[i][k] + d[k][j]) {
   d[i][j] = d[i][k] + d[k][j];
   prev[i][j]=k;
   }
  }
  }
 }

 //四舍五入距离
 for (int i = 0; i < v; i++) {
  for (int j = 0; j < v; j++) {
  d[i][j] = new bigdecimal(d[i][j]).setscale(2,
   roundingmode.half_up).doublevalue();
  }
 }

 //检测负权重环的方式很简单,就是判断所有i->i的距离d[i][i],如果存在小于0的,表示这个i->i的环路的权重和形成了一个负值,也就是存在这个负权重
 //在之前的其他最短路径算法中,无法通过这个方法来检测负环,因为之前路径距离都是保存在一个一维数组中,相等于只能检测d[0][0],无法检测每个d[i][i]
 for(int i=0;i<v;i++){
  if(d[i][i]<0)
  negativecycle=true;
 }
 }

 /**
 * 
 * @title: hasnegativecycle
 * @description: 是否拥有负权重环
 * @param @return 设定文件
 * @return boolean 返回类型
 * @throws
 */
 public boolean hasnegativecycle() {
 return negativecycle;
 }

 /**
 * 
 * @title: distto
 * @description: a->b最短路径的距离
 * @param @param a
 * @param @param b
 * @param @return 设定文件
 * @return double 返回类型
 * @throws
 */
 public double distto(int a, int b) {
 if (hasnegativecycle())
  throw new runtimeexception("有负权重环,不存在最短路径");
 return d[a][b];
 }
 
 /**
 * 
 * @title: printshortestpath
 * @description: 打印a->b最短路径
 * @param @return 设定文件
 * @return iterable<integer> 返回类型
 * @throws
 */
 public boolean printshortestpath(int a,int b){
 if (hasnegativecycle()){
  system.out.print("有负权重环,不存在最短路径");
 }else if(a==b)
  system.out.println(a+"->"+b);
 else{
  system.out.print(a+"->");
  path(a,b);
  system.out.print(b);
 }
 return true;
 }

 private void path(int a, int b) {
 int k=prev[a][b];
 
 if(k==-1){
  return;
 }
 
 path(a,k);
 system.out.print(k+"->");
 path(k,b);
 }
 
 

 /**
 * 
 * @title: addedge
 * @description: 添加边
 * @param @param a
 * @param @param b
 * @param @param w 设定文件
 * @return void 返回类型
 * @throws
 */
 public void addedge(int a, int b, double w) {
 d[a][b] = w;
 }

 public static void main(string[] args) {
 // 不含负权重环的文本数据
 string text1 = "f:\\算法\\attach\\tinyewdn.txt";
 // 含有负权重环的文本数据
 string text2 = "f:\\算法\\attach\\tinyewdnc.txt";

 in in = new in(text1);

 int n = in.readint();
 floydwarshall f = new floydwarshall(n);

 int e = in.readint();

 for (int i = 0; i < e; i++) {
  f.addedge(in.readint(), in.readint(), in.readdouble());
 }

 f.findshortestpath();

 int s = 0;
 for (int i = 0; i < n; i++) {
  system.out.println(s + "到" + i + "的距离为:" + f.distto(s, i));
  f.printshortestpath(s, i);
  system.out.println();
 }
 }

}

如果采用负权重环图,则会抛出异常,提示负环并表示无最短路径

如果采用不含负环的图,则会打印如下内容(目前以s=0作测试,其他点作为原点的最短路径可以自行尝试):

0到0的距离为:0.0
0->0

0到1的距离为:0.93
0->2->7->3->6->4->5->1
0到2的距离为:0.26
0->2
0到3的距离为:0.99
0->2->7->3
0到4的距离为:0.26
0->2->7->3->6->4
0到5的距离为:0.61
0->2->7->3->6->4->5
0到6的距离为:1.51
0->2->7->3->6
0到7的距离为:0.6
0->2->7

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!