java算法導(dǎo)論之FloydWarshall算法實(shí)現(xiàn)代碼
摘要: 算法導(dǎo)論之FloydWarshall算法
求一個(gè)圖中任意兩點(diǎn)之間的最短路徑
FloydWarshall算法是通過動(dòng)態(tài)規(guī)劃來計(jì)算任意兩點(diǎn)之間的最短路徑
如果普通求最短路徑,可以對(duì)圖進(jìn)行V次(頂點(diǎn)數(shù))BellmanFord算法。 這樣的話時(shí)間復(fù)雜度為EV^2
如果是稀疏圖,則近似于V^3
但是如果是密集圖,則時(shí)間復(fù)雜度會(huì)近似達(dá)到V^4,這種情況需要優(yōu)化,這里FloydWarshall通過動(dòng)態(tài)規(guī)劃進(jìn)行優(yōu)化
,并且使用鄰接矩陣來表示圖。
實(shí)例代碼:
package org.loda.graph;
import java.math.BigDecimal;
import java.math.RoundingMode;
import org.loda.util.In;
/**
*
* @ClassName: FloydWarshall
* @Description: 求一個(gè)圖中任意兩點(diǎn)之間的最短路徑
*
* FloydWarshall算法是通過動(dòng)態(tài)規(guī)劃來計(jì)算任意兩點(diǎn)之間的最短路徑
*
* 如果普通求最短路徑,可以對(duì)圖進(jìn)行V次(頂點(diǎn)數(shù))BellmanFord算法。 這樣的話時(shí)間復(fù)雜度為EV^2
* 如果是稀疏圖,則近似于V^3
* 但是如果是密集圖,則時(shí)間復(fù)雜度會(huì)近似達(dá)到V^4,這種情況需要優(yōu)化,這里FloydWarshall通過動(dòng)態(tài)規(guī)劃進(jìn)行優(yōu)化
* ,并且使用鄰接矩陣來表示圖。
* 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];
// 默認(rèn)設(shè)置所有節(jié)點(diǎn)都不可達(dá),而自己到自己是可達(dá)并且距離為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 設(shè)定文件
* @return void 返回類型
* @throws
*/
public void findShortestPath() {
//查找最短路徑
for (int k = 0; k < v; k++) {
//將每個(gè)k值考慮成i->j路徑中的一個(gè)中間點(diǎn)
for (int i = 0; i < v; i++) {
for (int j = 0; j < v; j++) {
//如果存在使得權(quán)重和更小的中間值k,就更新最短路徑為經(jīng)過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();
}
}
//檢測(cè)負(fù)權(quán)重環(huán)的方式很簡(jiǎn)單,就是判斷所有i->i的距離d[i][i],如果存在小于0的,表示這個(gè)i->i的環(huán)路的權(quán)重和形成了一個(gè)負(fù)值,也就是存在這個(gè)負(fù)權(quán)重
//在之前的其他最短路徑算法中,無法通過這個(gè)方法來檢測(cè)負(fù)環(huán),因?yàn)橹奥窂骄嚯x都是保存在一個(gè)一維數(shù)組中,相等于只能檢測(cè)d[0][0],無法檢測(cè)每個(gè)d[i][i]
for(int i=0;i<v;i++){
if(d[i][i]<0)
negativeCycle=true;
}
}
/**
*
* @Title: hasNegativeCycle
* @Description: 是否擁有負(fù)權(quán)重環(huán)
* @param @return 設(shè)定文件
* @return boolean 返回類型
* @throws
*/
public boolean hasNegativeCycle() {
return negativeCycle;
}
/**
*
* @Title: distTo
* @Description: a->b最短路徑的距離
* @param @param a
* @param @param b
* @param @return 設(shè)定文件
* @return double 返回類型
* @throws
*/
public double distTo(int a, int b) {
if (hasNegativeCycle())
throw new RuntimeException("有負(fù)權(quán)重環(huán),不存在最短路徑");
return d[a][b];
}
/**
*
* @Title: printShortestPath
* @Description: 打印a->b最短路徑
* @param @return 設(shè)定文件
* @return Iterable<Integer> 返回類型
* @throws
*/
public boolean printShortestPath(int a,int b){
if (hasNegativeCycle()){
System.out.print("有負(fù)權(quán)重環(huán),不存在最短路徑");
}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 設(shè)定文件
* @return void 返回類型
* @throws
*/
public void addEdge(int a, int b, double w) {
d[a][b] = w;
}
public static void main(String[] args) {
// 不含負(fù)權(quán)重環(huán)的文本數(shù)據(jù)
String text1 = "F:\\算法\\attach\\tinyEWDn.txt";
// 含有負(fù)權(quán)重環(huán)的文本數(shù)據(jù)
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();
}
}
}
如果采用負(fù)權(quán)重環(huán)圖,則會(huì)拋出異常,提示負(fù)環(huán)并表示無最短路徑
如果采用不含負(fù)環(huán)的圖,則會(huì)打印如下內(nèi)容(目前以s=0作測(cè)試,其他點(diǎn)作為原點(diǎn)的最短路徑可以自行嘗試):
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
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
- Java算法之?dāng)?shù)組冒泡排序代碼實(shí)例講解
- Java算法之串的簡(jiǎn)單處理
- Java算法實(shí)現(xiàn)調(diào)整數(shù)組順序使奇數(shù)位于偶數(shù)之前的講解
- Java算法實(shí)現(xiàn)楊輝三角的講解
- Java算法之冒泡排序?qū)嵗a
- Java算法之最長(zhǎng)公共子序列問題(LCS)實(shí)例分析
- java算法實(shí)現(xiàn)紅黑樹完整代碼示例
- Java算法之堆排序代碼示例
- java算法之二分查找法的實(shí)例詳解
- java算法實(shí)現(xiàn)預(yù)測(cè)雙色球中獎(jiǎng)號(hào)碼
- Java算法之遞歸算法計(jì)算階乘
- JAVA算法起步之插入排序?qū)嵗?/a>
- JAVA算法起步之堆排序?qū)嵗?/a>
- JAVA算法起步之快速排序?qū)嵗?/a>
- 關(guān)于各種排列組合java算法實(shí)現(xiàn)方法
- Java算法之時(shí)間復(fù)雜度和空間復(fù)雜度的概念和計(jì)算
相關(guān)文章
SpringMVC源碼解讀之HandlerMapping - AbstractUrlHandlerMapping系列re
這篇文章主要介紹了SpringMVC源碼解讀之HandlerMapping - AbstractUrlHandlerMapping系列request分發(fā) 的相關(guān)資料,需要的朋友可以參考下2016-02-02
Java While循環(huán) do-while循環(huán)用法
循環(huán)語句就是讓計(jì)算機(jī)根據(jù)條件做循環(huán)計(jì)算,在條件滿足時(shí)繼續(xù)循環(huán),條件不滿足時(shí)退出循環(huán),需要的朋友可以參考下2020-11-11
Java設(shè)計(jì)模式——工廠設(shè)計(jì)模式詳解
這篇文章主要介紹了Java設(shè)計(jì)模式——工廠設(shè)計(jì)模式詳解,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11
SSM框架下實(shí)現(xiàn)登錄注冊(cè)的示例代碼
這篇文章主要介紹了SSM框架下實(shí)現(xiàn)登錄注冊(cè)的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
詳解IDEA多module項(xiàng)目maven依賴的一些說明
這篇文章主要介紹了詳解IDEA多module項(xiàng)目maven依賴的一些說明,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-10-10
Java數(shù)據(jù)結(jié)構(gòu)之線索化二叉樹的實(shí)現(xiàn)
在二叉樹的結(jié)點(diǎn)上加上線索的二叉樹稱為線索二叉樹,對(duì)二叉樹以某種遍歷方式進(jìn)行遍歷,使其變?yōu)榫€索二叉樹的過程稱為對(duì)二叉樹進(jìn)行線索化。本文將詳解如何實(shí)現(xiàn)線索化二叉樹,需要的可以參考一下2022-05-05
Java設(shè)計(jì)模式之命令模式(Command模式)介紹
這篇文章主要介紹了Java設(shè)計(jì)模式之命令模式(Command模式)介紹,本文講解了Command模式的定義、如何使用命令模式等內(nèi)容,需要的朋友可以參考下2015-03-03

