算法导论——所有点对的最短路径: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
时间: 2024-11-06 09:49:05

算法导论——所有点对的最短路径:FloydWarshall算法的相关文章

多源最短路径---Floyd-Warshall算法

摘自啊哈算法-知识分享,代码自己有改动,使得输出更直观. 小哼准备去一些城市旅游.有些城市之间有公路,有些城市之间没有,如下图.为了节省经费以及方便计划旅程,小哼希望出发之前知道任意两个城市之间的最短路程. 上图中有四个城市8条公路,公路上的数字表示该公路的长短.现在需要求任意两个城市之间的最短路径,也就是求任意两点之间的最短路径."多源路径最短"问题. 现在需要一个数据结构来存储图的信息,用一个4*4的矩阵来存储.比如城市1到城市2的路程为2,则设e[1][2]的值为2.2号城市无法

算法导论学习之快排+各种排序算法时间复杂度总结

快排是一种最常用的排序算法,因为其平均的时间复杂度是nlgn,并且其中的常数因子比较小. 一.快速排序 快排和合并排序一样都是基于分治的排序算法;快排的分治如下: 分解:对区间A[p,r]进行分解,返回q,使得A[p–q-1]都不大于A[q] A[q+1,r]都大于A[q]; 求解:对上面得到的区间继续递归进行快排 合并:因为快排是原地排序,所以不需要特别的合并 从上可以看出最重要的就是分解函数,其按关键值将数组划分成3部分,其具体实现的过程见代码注释. 我们一般取数组的最后一个元素作为划分比较

算法导论之七(中位数和顺序统计量之选择算法)

实际生活中,我们经常会遇到这类问题:在一个集合,谁是最大的元素?谁是最小的元素?或者谁是第二小的元素?....等等.那么如何在较短的时间内解决这类问题,就是本文要阐述的. 先来熟悉几个概念: 1.顺序统计量: 在一个由n个元素组成的集合中,第i个顺序统计量(order statistic)是该集合中第i小的元素.最小值是第1个顺序统计量(i=1),最大值是第n个顺序统计量(i=n).   2.中位数: 一个中位数是它所属集合的"中点元素",当n为奇数时,中位数是唯一的,位于i=(n+1

算法导论 第十六章:贪心算法之单任务调度问题

贪心算法是使所做的选择看起来都是当前最优的,通过所做的局部最优选择来产生一个全局最优解. 其具有的性质如下: 1)贪心选择性质:一个全局最优解可以通过局部最优(贪心)选择来达到.即,在考虑如何做选择时,我们只考虑对当前问题最佳的选择而不考虑子问题的结果. 这一点是贪心算法不同于动态规划之处:在动态规划中,每一步都要做出选择,但是这些选择依赖于子问题的解.因此,解动态规划问题一般是自底向上,从小问题处理至大问题.在贪心算法中,我们所做的总是当前看似最优的选择,然后再解决选择之后所出现的子问题.贪心

算法导论笔记——第十六章 贪心算法

通常用于最优化问题,我们做出一组选择来达到最优解.每步都追求局部最优.对很多问题都能求得最优解,而且速度比动态规划方法快得多. 16.1 活动选择问题 按结束时间排序,然后选择兼容活动. 定理16.1 考虑任意非空子问题Sk,令am是Sk中结束时间最早的活动,则am在Sk的某个最大兼容活动子集中. 16.2 贪心算法原理 设计贪心算法步骤: 1>将最优化问题转化为这样的形式:对其做出一次选择后,只剩下一个子问题需要求解. 2>证明作出贪心选择后,原问题总是存在最优解,即贪心选择总是安全的. 3

算法导论学习之线性时间排序+排序算法稳定性终结

前面我们学习的几种排序算法都是基于比较的,对于任何输入数据他们都是适用的,其最坏的时间复杂度不会低于nlgn: 但对于一些比较特殊的输入数据,我们可以不采取比较的方法而是采用其它的方法对其进行排序,以达到线性的时间复杂度.下面就来介绍三种这样的算法:计数排序,基数排序,桶排序(因为这几种算法不常见,我只实现了计数排序,其它两种排序用伪代码表示). 一.计数排序 算法思想:给定n个位于0–k之间的数(k是一个不太大的整数),我们可以统计出每个数前面有多少个小于它的数,然后就可以直接确定这个数在数组

《算法导论》学习笔记第一章(算法在计算中的作用)

第一章主要讲了一些概念,什么是算法,为什么算法值得研究. 印象比较深的是章节的最后一句,是否具有算法知识与技术的坚实基础是区分真正熟练的程序员与初学者的一个特征.使用现代计算技术,如果你对算法懂得不多,你也可以完成一些任务,但是,如果有一个好的算法背景,那么你可以做的事情就多得多. Having a solid base of algorithmic knowledge and technique is one characteristic that separates the truly sk

《算法导论》图相关算法小结

最近又抽空读了一遍<算法导论>,关于图的内容贯穿了多个章节(比如在动态规划一章埋了无权最短路径的伏笔,后面才专门讲),适用条件各异,而且都有证明过程. 如果不打算熟记证明,仅仅是应用,遇到具体场景再去回忆适用于哪种算法不太方便. 以下内容以手头的机械工业出版社基于原书第2版的译本整理了一下,便于速查. 不包含思考题.标注为"*"的章节和习题内容. 符号定义 一般地, 图G=(V, E),其中V代表顶点集合,E代表边集合.ω(u, v)代表从顶点u到顶点v的边的权值. 如果ω

算法导论 学习资源

学习的过程会遇到些问题,发现了一些比较好的资源,每章都会看下别人写的总结,自己太懒了,先记录下别人写的吧,呵呵. 1  Tanky Woo的,每次差不多都看他的 <算法导论>学习总结 - 1.前言 <算法导论>学习总结 - 2.第一章 && 第二章 && 第三章 <算法导论>学习总结 - 3.第四章 && 第五章 <算法导论>学习总结 - 4.第六章(1) 堆排序 <算法导论>学习总结 - 5.第六