十一期間看到了以下幾個類似的問題,最開始也是有點混淆的狀態,這里做一下簡單的學習記錄,希望可以為有同樣問題的小伙伴提供幫助,篇幅比較長,建議收藏后再閱讀。 問題描述如下(這里以簡單得連接圖表示各個連接信息): 1.有7個村莊(A, B, C, D, E, F, G) ,現在需要修路把7個村莊連通,各個村莊的距離用邊線表示(權) ,比如 A – B 距離 5公里,問:如何修路保證各個村莊都能連通,并且總的修建公路總里程最短? 2.與1的問題是一樣的,某城市新增7個站點(A, B, C, D, E, F, G) ,現在需要修路把7個站點連通,各個站點的距離用邊線表示(權) ,比如 A – B 距離 12公里,問:如何修路保證各個站點都能連通,并且總的修建公路總里程最短? 3.有7個村莊(A, B, C, D, E, F, G) ,現在有六個郵差,從G點出發,需要分別把郵件分別送到 A, B, C , D, E, F 六個村莊,各個村莊的距離用邊線表示(權) ,比如 A – B 距離 5公里 問:如何計算出G村莊到 其它各個村莊的最短距離? 如果從其它點出發到各個點的最短距離又是多少? 4.有7個村莊(A, B, C, D, E, F, G),各個村莊的距離用邊線表示(權) ,比如 A – B 距離 5公里 問:如何計算出各村莊到 其它各村莊的最短距離? 其實以上四個問題的描述可以簡述成以下三個類別: 1.求各個點都連接且權值最小的路線(問題1和問題2); 2.以某一個點作為出發點,求這個點到其它各個點的權值最小的路線(問題3,可以看成是問題4的一個子集); 3.以每一個點作為出發點,求這些點到其它各個點的權值最小的路線(問題4,可以看成是問題3 的一個全集)。
問題的描述很簡單,其實都可以歸類為修路問題,需要考慮采用什么方式對問題進行求解。修路問題本質就是就是最小生成樹問題,什么是最小生成樹(Minimum Cost Spanning Tree,簡稱MST)?給定一個帶權的無向連通圖,如何選取一棵生成樹,使樹上所有邊上權的總和為最小,這叫最小生成樹。最小生成樹有以下特點: (1)N個頂點,一定有N-1條邊 (2)包含全部頂點 (3)N-1條邊都在圖中 求最小生成樹的算法主要是普里姆算法和克魯斯卡爾算法。
普里姆算法
1)普利姆(Prim)算法求最小生成樹,也就是在包含n個頂點的連通圖中,找出只有(n-1)條邊包含所有n個頂點的連通子圖,也就是所謂的極小連通子圖; 2)普利姆的算法如下: (1)設G=(V,E)是連通網,T=(U,D)是最小生成樹,V,U是頂點集合,E,D是邊的集合; (2)若從頂點u開始構造最小生成樹,則從集合V中取出頂點u放入集合U中,標記頂點v的visited[u]=1; (3)若集合U中頂點ui與集合V-U中的頂點vj之間存在邊,則尋找這些邊中權值最小的邊,但不能構成回路,將頂點vj加入集合U中,將邊(ui,vj)加入集合D中,標記visited[vj]=1; (4)重復步驟(2),直到U與V相等,即所有頂點都被標記為訪問過,此時D中有n-1條邊。
以此算法來分析問題1。假設從點A 開始,獲取所有連接的路線,每次取最小的路線及此路線的另一個點加入,以此循環,步驟大致如下: ①從A頂點開始處理 A-C [7] A-G[2] A-B[5]=> <A,G> 2 ② <A,G> 開始 , 將A 和 G 頂點和他們相鄰的還沒有訪問的頂點進行處理 A-C[7] A-B[5] G-B[3] G-E[4] G-F[6]=》<A,G,B> ③ <A,G,B> 開始,將A,G,B 頂點 和他們相鄰的還沒有訪問的頂點進行處理 A-C[7] G-E[4] G-F[6] B-D[9]=><A,G,B,E> ④{A,G,B,E}->F加入, 對應 邊<E,F> 權值:5 ⑤{A,G,B,E,F}->D加入 , 對應 邊<F,D> 權值:4 ⑥{A,G,B,E,F,D}->C加入 , 對應 邊<A,C> 權值:7 ===> <A,G,B,E,F,D,C> 有了解決問題的思路,接下來用代碼直接對問題進行求解。
public class PrimAlgorithm { public static void main ( String
[ ] args
) { char
[ ] data
= new char [ ] { 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' } ; int dataNum
= data
. length
; int
[ ] [ ] weight
= new int [ ] [ ] { { 10000 , 5 , 7 , 10000 , 10000 , 10000 , 2 } , { 5 , 10000 , 10000 , 9 , 10000 , 10000 , 3 } , { 7 , 10000 , 10000 , 10000 , 8 , 10000 , 10000 } , { 10000 , 9 , 10000 , 10000 , 10000 , 4 , 10000 } , { 10000 , 10000 , 8 , 10000 , 10000 , 5 , 4 } , { 10000 , 10000 , 10000 , 4 , 5 , 10000 , 6 } , { 2 , 3 , 10000 , 10000 , 4 , 6 , 10000 } , } ; Graph graph
= new Graph ( dataNum
) ; MinTrees minTrees
= new MinTrees ( ) ; minTrees
. createGraph ( graph
, data
, weight
) ; minTrees
. showGraph ( graph
) ; int num
= 0 ; while ( true ) { System
. out
. println ( "請輸入從哪個點開始('A', 'B', 'C', 'D', 'E', 'F', 'G'):" ) ; Scanner sc
= new Scanner ( System
. in ) ; String str
= sc
. next ( ) ; switch ( str
) { case "A" : num
= 0 ; break ; case "B" : num
= 1 ; break ; case "C" : num
= 2 ; break ; case "D" : num
= 3 ; break ; case "E" : num
= 4 ; break ; case "F" : num
= 5 ; break ; case "G" : num
= 6 ; break ; default : break ; } minTrees
. prim ( graph
, num
) ; } } } class MinTrees { public void createGraph ( Graph graph
, char
[ ] data
, int
[ ] [ ] weight
) { for ( int i
= 0 ; i
< data
. length
; i
++ ) { graph
. data
[ i
] = data
[ i
] ; for ( int j
= 0 ; j
< data
. length
; j
++ ) { graph
. weight
[ i
] [ j
] = weight
[ i
] [ j
] ; } } } public void showGraph ( Graph graph
) { for ( int
[ ] link
: graph
. weight
) { System
. out
. println ( Arrays
. toString ( link
) ) ; } } public void prim ( Graph graph
, int index
) { int h1
= - 1 ; int h2
= - 1 ; int
[ ] visited
= new int [ graph
. dataNum
] ; visited
[ index
] = 1 ; int min
= 10000 ; for ( int k
= 1 ; k
< graph
. dataNum
; k
++ ) { for ( int i
= 0 ; i
< graph
. dataNum
; i
++ ) { for ( int j
= 0 ; j
< graph
. dataNum
; j
++ ) { if ( visited
[ i
] == 1 && visited
[ j
] == 0 && graph
. weight
[ i
] [ j
] < min
) { min
= graph
. weight
[ i
] [ j
] ; h1
= i
; h2
= j
; } } } System
. out
. println ( "邊<" + graph
. data
[ h1
] + "," + graph
. data
[ h2
] + "> 權值:" + min
) ; visited
[ h2
] = 1 ; min
= 10000 ; } }
} class Graph { int dataNum
; char
[ ] data
; int
[ ] [ ] weight
; public Graph ( int dataNum
) { this . dataNum
= dataNum
; this . data
= new char [ dataNum
] ; this . weight
= new int [ dataNum
] [ dataNum
] ; }
}
我們看一下輸出結果(只選取了A和B,結果正確,其他的讀者可以自行驗證):
請輸入從哪個點開始(
'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' ):
A
邊
< A , G > 權值
: 2
邊
< G , B > 權值
: 3
邊
< G , E > 權值
: 4
邊
< E , F > 權值
: 5
邊
< F , D > 權值
: 4
邊
< A , C > 權值
: 7
請輸入從哪個點開始(
'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' ):
B
邊
< B , G > 權值
: 3
邊
< G , A > 權值
: 2
邊
< G , E > 權值
: 4
邊
< E , F > 權值
: 5
邊
< F , D > 權值
: 4
邊
< A , C > 權值
: 7
當然,問題2依然可以使用普里姆算法進行求解,讀者有需要可以自行使用該方法求解,這里給大家介紹另外一種求解修路問題的方法:克魯斯卡爾算法,我們將對問題2使用克魯斯卡爾算法進行求解。
克魯斯卡爾算法
基本思想:按照權值從小到大的順序選擇n-1條邊,并保證這n-1條邊不構成回路。 具體做法:首先構造一個只含n個頂點的森林,然后依權值從小到大從連通網中選擇邊加入到森林中,并使森林中不產生回路,直至森林變成一棵樹為止。 例如,對于如問題2的所示的連通網可以有多棵權值總和不相同的生成樹:
而我們要選取的是權值最小的一個路線。以問題2中的圖為例,來對克魯斯卡爾進行演示(假設,用數組R保存最小生成樹結果): 第1步:將邊<E,F>加入R中。 邊<E,F>的權值最小,因此將它加入到最小生成樹結果R中。 第2步:將邊<C,D>加入R中。 上一步操作之后,邊<C,D>的權值最小,因此將它加入到最小生成樹結果R中。 第3步:將邊<D,E>加入R中。 上一步操作之后,邊<D,E>的權值最小,因此將它加入到最小生成樹結果R中。 第4步:將邊<B,F>加入R中。 上一步操作之后,邊<C,E>的權值最小,但<C,E>會和已有的邊構成回路;因此,跳過邊<C,E>。同理,跳過邊<C,F>。將邊<B,F>加入到最小生成樹結果R中。 第5步:將邊<E,G>加入R中。 上一步操作之后,邊<E,G>的權值最小,因此將它加入到最小生成樹結果R中。 第6步:將邊<A,B>加入R中。 上一步操作之后,邊<F,G>的權值最小,但<F,G>會和已有的邊構成回路;因此,跳過邊<F,G>。同理,跳過邊<B,C>。將邊<A,B>加入到最小生成樹結果R中。 此時,最小生成樹構造完成!它包括的邊依次是:<E,F> <C,D> <D,E> <B,F> <E,G> <A,B>。 根據前面介紹的克魯斯卡爾算法的基本思想和做法,我們能夠了解到,克魯斯卡爾算法重點需要解決的以下兩個問題: 問題一,對圖的所有邊按照權值大小進行排序;問題二,將邊添加到最小生成樹中時,怎么樣判斷是否形成了回路。 問題一很好解決,采用排序算法進行排序即可。問題二處理方式是:記錄頂點在"最小生成樹"中的終點,頂點的終點是"在最小生成樹中與它連通的最大頂點"。然后每次需要將一條邊添加到最小生存樹時,判斷該邊的兩個頂點的終點是否重合,重合的話則會構成回路。 以上圖為例,在將<E,F> <C,D> <D,E>加入到最小生成樹R中之后,這幾條邊的頂點就都有了終點: C的終點是F, D的終點是F,E的終點是F,F的終點是F。將所有頂點按照從小到大的順序排列好之后,某個頂點的終點就是"與它連通的最大頂點"。 因此,接下來,雖然<C,E>是權值最小的邊。但是C和E的終點都是F,即它們的終點相同,因此,將<C,E>加入最小生成樹的話,會形成回路。這就是判斷回路的方式。也就是說,我們加入的邊的兩個頂點不能都指向同一個終點,否則將構成回路。 有了解決問題的思路,接下來用代碼直接對問題進行求解。
public class KruskalCase { private int edgeNum
; private char
[ ] vertexs
; private int
[ ] [ ] matrix
; private static final int
INF = Integer
. MAX_VALUE ; public static void main ( String
[ ] args
) { char
[ ] vertexs
= { 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' } ; int matrix
[ ] [ ] = { { 0 , 12 , INF , INF , INF , 16 , 14 } , { 12 , 0 , 10 , INF , INF , 7 , INF } , { INF , 10 , 0 , 3 , 5 , 6 , INF } , { INF , INF , 3 , 0 , 4 , INF , INF } , { INF , INF , 5 , 4 , 0 , 2 , 8 } , { 16 , 7 , 6 , INF , 2 , 0 , 9 } , { 14 , INF , INF , INF , 8 , 9 , 0 } } ; KruskalCase kruskalCase
= new KruskalCase ( vertexs
, matrix
) ; kruskalCase
. kruskal ( ) ; } public KruskalCase ( char
[ ] vertexs
, int
[ ] [ ] matrix
) { this . vertexs
= vertexs
; this . matrix
= matrix
; for ( int i
= 0 ; i
< vlen
; i
++ ) { for ( int j
= i
+ 1 ; j
< vlen
; j
++ ) { if ( this . matrix
[ i
] [ j
] != INF ) { edgeNum
++ ; } } } } public void kruskal ( ) { int index
= 0 ; int
[ ] ends
= new int [ edgeNum
] ; EData
[ ] rets
= new EData [ edgeNum
] ; EData
[ ] edges
= getEdges ( ) ; System
. out
. println ( "圖的邊的集合=" + Arrays
. toString ( edges
) + " 共" + edges
. length
) ; sortEdges ( edges
) ; for ( int i
= 0 ; i
< edgeNum
; i
++ ) { int p1
= getPosition ( edges
[ i
] . start
) ; int p2
= getPosition ( edges
[ i
] . end
) ; int m
= getEnd ( ends
, p1
) ; int n
= getEnd ( ends
, p2
) ; if ( m
!= n
) { ends
[ m
] = n
; rets
[ index
++ ] = edges
[ i
] ; } } System
. out
. println ( "最小生成樹為" ) ; for ( int i
= 0 ; i
< index
; i
++ ) { System
. out
. println ( rets
[ i
] ) ; } } private void sortEdges ( EData
[ ] edges
) { for ( int i
= 0 ; i
< edges
. length
- 1 ; i
++ ) { for ( int j
= 0 ; j
< edges
. length
- 1 - i
; j
++ ) { if ( edges
[ j
] . weight
> edges
[ j
+ 1 ] . weight
) { EData tmp
= edges
[ j
] ; edges
[ j
] = edges
[ j
+ 1 ] ; edges
[ j
+ 1 ] = tmp
; } } } } private int
getPosition ( char ch
) { for ( int i
= 0 ; i
< vertexs
. length
; i
++ ) { if ( vertexs
[ i
] == ch
) { return i
; } } return - 1 ; } private EData
[ ] getEdges ( ) { int index
= 0 ; EData
[ ] edges
= new EData [ edgeNum
] ; for ( int i
= 0 ; i
< vertexs
. length
; i
++ ) { for ( int j
= i
+ 1 ; j
< vertexs
. length
; j
++ ) { if ( matrix
[ i
] [ j
] != INF ) { edges
[ index
++ ] = new EData ( vertexs
[ i
] , vertexs
[ j
] , matrix
[ i
] [ j
] ) ; } } } return edges
; } private int
getEnd ( int
[ ] ends
, int i
) { while ( ends
[ i
] != 0 ) { i
= ends
[ i
] ; } return i
; } }
class EData { char start
; char end
; int weight
; public EData ( char start
, char end
, int weight
) { this . start
= start
; this . end
= end
; this . weight
= weight
; } @Override
public String
toString ( ) { return "EData [<" + start
+ ", " + end
+ ">= " + weight
+ "]" ; }
}
我們看一下輸出結果,最小生成樹為:
EData
[ < E , F >= 2 ]
EData
[ < C , D >= 3 ]
EData
[ < D , E >= 4 ]
EData
[ < B , F >= 7 ]
EData
[ < E , G >= 8 ]
EData
[ < A , B >= 12 ]
即為問題2的求解。 問題1和問題2雖然問題一樣,但是兩種解法卻有不同的地方。使用普里姆算法時,我們可以指定從哪個點開始,但不管從哪個點開始,最終形成的路線是確定的;而克魯斯卡爾算法在解決問題的時候不用指定從哪個點開始,最終給出問題的解決路線。兩種方法雖然略有差別,但最后的性質是一樣的,讀者可以依據自己的需求和實際使用場景進行選擇。問題1和問題2只需要求出可以聯通的一個最短路線。如果是對于某一個點,要求這個點可以到達其他的任何一個點,求出滿足這樣條件下的最短路線又該如何求解呢? 迪杰斯特拉(Dijkstra)算法是典型最短路徑算法,用于計算一個結點到其他結點的最短路徑。 它的主要特點是以起始點為中心向外層層擴展(廣度優先搜索思想,不了解的同學可以自行百度,這里不做擴展),直到擴展到終點為止。
迪杰斯特拉(Dijkstra)算法
迪杰斯特拉(Dijkstra)算法過程是這樣的: ①設置出發頂點為v,頂點集合V{v1,v2,vi…},v到V中各頂點的距離構成距離集合Dis,Dis{d1,d2,di…},Dis集合記錄著v到圖中各頂點的距離(到自身可以看作0,v到vi距離對應為di) ②從Dis中選擇值最小的di并移出Dis集合,同時移出V集合中對應的頂點vi,此時的v到vi即為最短路徑 ③更新Dis集合,更新規則為:比較v到V集合中頂點的距離值,與v通過vi到V集合中頂點的距離值,保留值較小的一個(同時也應該更新頂點的前驅節點為vi,表明是通過vi到達的) ④重復執行②③步驟,直到最短路徑頂點為目標頂點即可結束 看步驟也許還不明白,這里我們依然以圖解的方式進行說明。這里假設以問題3中的G作為出發頂點,這里需要定義三個集合的類:
class VisitedVertex {
public int
[ ] already_arr
;
public int
[ ] pre_visited
;
public int
[ ] dis
;
}
在還沒有進行訪問前,需要初始化各個變量: 以G為出發頂點訪問過一次后的,變量的值變為: 將以上圖轉化以下,得到如下形式: 第一行表示already_arr,當前數據表示G已經被訪問過,第二行表示pre_visited,當前數據表示A,B,E,F的前驅結點都是G,第三行表示 dis,當前數據表示A到G距離2,B到G距離3,E到G距離4,F到G距離6,N表示當前點與G沒有直接相連。G 訪問完后,得到最小的距離點A,接下來 A點作為新的訪問頂點(注意不是出發頂點)重復上述步驟。這樣通過遍歷所有的頂點,即可得到問題的求解。 有了解決問題的思路,接下來用代碼直接對問題進行求解。
public class DijkstraAlgorithm { public static void main ( String
[ ] args
) { char
[ ] vertex
= { 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' } ; int
[ ] [ ] matrix
= new int [ vertex
. length
] [ vertex
. length
] ; final int
N = 65535 ; matrix
[ 0 ] = new int [ ] { N , 5 , 7 , N , N , N , 2 } ; matrix
[ 1 ] = new int [ ] { 5 , N , N , 9 , N , N , 3 } ; matrix
[ 2 ] = new int [ ] { 7 , N , N , N , 8 , N , N } ; matrix
[ 3 ] = new int [ ] { N , 9 , N , N , N , 4 , N } ; matrix
[ 4 ] = new int [ ] { N , N , 8 , N , N , 5 , 4 } ; matrix
[ 5 ] = new int [ ] { N , N , N , 4 , 5 , N , 6 } ; matrix
[ 6 ] = new int [ ] { 2 , 3 , N , N , 4 , 6 , N } ; Graph graph
= new Graph ( vertex
, matrix
) ; graph
. dsj ( 6 ) ; graph
. showDijkstra ( ) ; } } class Graph { private char
[ ] vertex
; private int
[ ] [ ] matrix
; private VisitedVertex vv
; public Graph ( char
[ ] vertex
, int
[ ] [ ] matrix
) { this . vertex
= vertex
; this . matrix
= matrix
; } public void showDijkstra ( ) { vv
. show ( ) ; } public void dsj ( int index
) { vv
= new VisitedVertex ( vertex
. length
, index
) ; update ( index
) ; for ( int j
= 1 ; j
< vertex
. length
; j
++ ) { index
= vv
. updateArr ( ) ; update ( index
) ; } } private void update ( int index
) { int len
= 0 ; for ( int j
= 0 ; j
< matrix
[ index
] . length
; j
++ ) { len
= vv
. getDis ( index
) + matrix
[ index
] [ j
] ; if ( ! vv
. in ( j
) && len
< vv
. getDis ( j
) ) { vv
. updatePre ( j
, index
) ; vv
. updateDis ( j
, len
) ; } } }
}
class VisitedVertex { public int
[ ] already_arr
; public int
[ ] pre_visited
; public int
[ ] dis
; public VisitedVertex ( int length
, int index
) { this . already_arr
= new int [ length
] ; this . pre_visited
= new int [ length
] ; this . dis
= new int [ length
] ; Arrays
. fill ( dis
, 65535 ) ; this . already_arr
[ index
] = 1 ; this . dis
[ index
] = 0 ; } public boolean
in ( int index
) { return already_arr
[ index
] == 1 ; } public void updateDis ( int index
, int len
) { dis
[ index
] = len
; } public void updatePre ( int pre
, int index
) { pre_visited
[ pre
] = index
; } public int
getDis ( int index
) { return dis
[ index
] ; } public int
updateArr ( ) { int min
= 65535 , index
= 0 ; for ( int i
= 0 ; i
< already_arr
. length
; i
++ ) { if ( already_arr
[ i
] == 0 && dis
[ i
] < min
) { min
= dis
[ i
] ; index
= i
; } } already_arr
[ index
] = 1 ; return index
; } public void show ( ) { char
[ ] vertex
= { 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' } ; int count
= 0 ; for ( int i
: dis
) { if ( i
!= 65535 ) { System
. out
. print ( vertex
[ count
] + "(" + i
+ ") " ) ; } else { System
. out
. println ( "N " ) ; } count
++ ; } System
. out
. println ( ) ; }
}
我們看一下輸出結果:
A ( 2 ) B ( 3 ) C ( 9 ) D ( 10 ) E ( 4 ) F ( 6 ) G ( 0 )
問題3只是針對某一個點到其他點的最短距離的求解,如果我們不滿足于某一個點,而是希望所有的點都需要一條到其他點的最短路徑,可以怎么處理呢?當然我們可以利用以上方式,每個點都使用一遍方法進行求解,但這樣不方便,如果點的數量足夠多的話,操作起來就會很麻煩。那么有沒有什么方法可以一次性得到每一個點到其他點的最短路徑的集合呢?這里就需要使用弗洛伊德算法。
弗洛伊德算法
和Dijkstra算法一樣,弗洛伊德(Floyd)算法也是一種用于尋找給定的加權圖中頂點間最短路徑的算法。該算法名稱以創始人之一、1978年圖靈獎獲得者、斯坦福大學計算機科學系教授羅伯特·弗洛伊德命名。 弗洛伊德算法與迪杰斯特拉算法有什么區別呢?弗洛伊德算法(Floyd)計算圖中各個頂點之間的最短路徑,迪杰斯特拉算法用于計算圖中某一個頂點到其他頂點的最短路徑。迪杰斯特拉算法通過選定的被訪問頂點,求出從出發訪問頂點到其他頂點的最短路徑;弗洛伊德算法中每一個頂點都是出發訪問點,所以需要將每一個頂點看做被訪問頂點,求出從每一個頂點到其他頂點的最短路徑。弗洛伊德算法的大致步驟如下: ①設置頂點vi到頂點vk的最短路徑已知為Lik,頂點vk到vj的最短路徑已知為Lkj,頂點vi到vj的路徑為Lij,則vi到vj的最短路徑為:min((Lik+Lkj),Lij),vk的取值為圖中所有頂點,則可獲得vi到vj的最短路徑; ②至于vi到vk的最短路徑Lik或者vk到vj的最短路徑Lkj,是以同樣的方式獲得。 以問題4為例,初始各點之間的距離表如圖所示:
將A作為中間頂點,可以有三種路線:. C-A-G [9],C-A-B [12],G-A-B [7]。把A作為中間頂點的所有情況都進行遍歷, 具體轉換過程為:以A頂點作為中間頂點,B->A->C的距離由N->9,同理C到B;C->A->G的距離由N->12,同理G到C;G->B的距離為3小于7,所以不用變。更換中間頂點,循環執行操作,直到所有頂點都作為中間頂點更新后,計算結束就會得到更新距離表 和 前驅關系。第一次更新后距離表和前驅關系為: 有了解決問題的思路,接下來用代碼直接對問題進行求解。
public class FloydAlgorithm { public static void main ( String
[ ] args
) { char
[ ] vertex
= { 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' } ; int
[ ] [ ] matrix
= new int [ vertex
. length
] [ vertex
. length
] ; final int
N = 65535 ; matrix
[ 0 ] = new int [ ] { 0 , 5 , 7 , N , N , N , 2 } ; matrix
[ 1 ] = new int [ ] { 5 , 0 , N , 9 , N , N , 3 } ; matrix
[ 2 ] = new int [ ] { 7 , N , 0 , N , 8 , N , N } ; matrix
[ 3 ] = new int [ ] { N , 9 , N , 0 , N , 4 , N } ; matrix
[ 4 ] = new int [ ] { N , N , 8 , N , 0 , 5 , 4 } ; matrix
[ 5 ] = new int [ ] { N , N , N , 4 , 5 , 0 , 6 } ; matrix
[ 6 ] = new int [ ] { 2 , 3 , N , N , 4 , 6 , 0 } ; Graph graph
= new Graph ( vertex
. length
, matrix
, vertex
) ; graph
. floyd ( ) ; graph
. show ( ) ; } }
class Graph { private char
[ ] vertex
; private int
[ ] [ ] dis
; private int
[ ] [ ] pre
; public Graph ( int length
, int
[ ] [ ] matrix
, char
[ ] vertex
) { this . vertex
= vertex
; this . dis
= matrix
; this . pre
= new int [ length
] [ length
] ; for ( int i
= 0 ; i
< length
; i
++ ) { Arrays
. fill ( pre
[ i
] , i
) ; } } public void show ( ) { char
[ ] vertex
= { 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' } ; for ( int k
= 0 ; k
< dis
. length
; k
++ ) { for ( int i
= 0 ; i
< dis
. length
; i
++ ) { System
. out
. print ( "(" + vertex
[ k
] + "->" + vertex
[ i
] + ":" + dis
[ k
] [ i
] + ") " ) ; } } } public void floyd ( ) { int len
= 0 ; for ( int k
= 0 ; k
< dis
. length
; k
++ ) { for ( int i
= 0 ; i
< dis
. length
; i
++ ) { for ( int j
= 0 ; j
< dis
. length
; j
++ ) { len
= dis
[ i
] [ k
] + dis
[ k
] [ j
] ; if ( len
< dis
[ i
] [ j
] ) { dis
[ i
] [ j
] = len
; pre
[ i
] [ j
] = pre
[ k
] [ j
] ; } } } } }
}
最后結果為:
A 到其他點的最短路徑集合
( A - > A : 0 ) ( A - > B : 5 ) ( A - > C : 7 ) ( A - > D : 12 ) ( A - > E : 6 ) ( A - > F : 8 ) ( A - > G : 2 ) B 到其他點的最短路徑集合
( B - > A : 5 ) ( B - > B : 0 ) ( B - > C : 12 ) ( B - > D : 9 ) ( B - > E : 7 ) ( B - > F : 9 ) ( B - > G : 3 ) C 到其他點的最短路徑集合
( C - > A : 7 ) ( C - > B : 12 ) ( C - > C : 0 ) ( C - > D : 17 ) ( C - > E : 8 ) ( C - > F : 13 ) ( C - > G : 9 ) D 到其他點的最短路徑集合
( D - > A : 12 ) ( D - > B : 9 ) ( D - > C : 17 ) ( D - > D : 0 ) ( D - > E : 9 ) ( D - > F : 4 ) ( D - > G : 10 ) E 到其他點的最短路徑集合
( E - > A : 6 ) ( E - > B : 7 ) ( E - > C : 8 ) ( E - > D : 9 ) ( E - > E : 0 ) ( E - > F : 5 ) ( E - > G : 4 ) F 到其他點的最短路徑集合
( F - > A : 8 ) ( F - > B : 9 ) ( F - > C : 13 ) ( F - > D : 4 ) ( F - > E : 5 ) ( F - > F : 0 ) ( F - > G : 6 ) G 到其他點的最短路徑集合
( G - > A : 2 ) ( G - > B : 3 ) ( G - > C : 9 ) ( G - > D : 10 ) ( G - > E : 4 ) ( G - > F : 6 ) ( G - > G : 0 )
至此,以上問題已全部得到解決,同時對于普里姆算法,克魯斯卡爾算法,弗洛伊德算法與迪杰斯特拉算法的原理和解法進行了詳細的分析,希望對大家有所幫助。
總結
以上是生活随笔 為你收集整理的修路问题算法的总结 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。