數據結構 -- 圖的最短路徑 Java版


  作者版權所有,轉載請注明出處,多謝.http://www.cnblogs.com/Henvealf/p/5574455.html

  上一篇介紹了有關圖的表示和遍歷實現.數據結構 -- 簡單圖的實現與遍歷 (Java)現在就來看看關於求圖的最短路徑的問題:

  注意:本人學習圖的時候看的書是:

    <<數據結構與算法 Java語言版>> (美)Adam Drozdek/著 周翔/譯 機械工業出版社出版

  由於要仔細講解內容過多並且本人水平有限,推薦大家找出這本書來看,本篇文章主要是對其中Dijkstra 算法,Ford 算法 ,通用型的糾正標記算法這三個偽代碼的實現.開始頁數為P284.

1.Dijkstra 算法

  首先來看一下 Dijkstra 算法,它不能夠處理權值為負的圖.本算法的主要步驟:

  

  1.找出距離起始頂點距離最短的頂點,這里設為頂點nowVertice.

  2.遍歷所有與頂點nowVertice相鄰的頂點nextVertice.如果發現選擇nowVertice到達nextVertice的路徑后,nextVertice距離起始頂點的距離比當前的距離小.便更新新的距離.如下:

    if(currDist[nextVertice] > currDist[nowVertice] + weight) {            //weight為從nowVertice到nextVertice說需要的權重
                    currDist[nextVertice] = currDist[nowVertice] + weight;
     }

    currDist是一個全局數組,currDist[i]意思就是當前起始頂點到頂點i的距離.

  3.將nowVertice從圖中刪除.

  4.重復步驟1,直到所有的頂點都被刪除完.

 

  補充,在實現的時候,上面說的刪除並不是真的直接從圖中把某一頂點刪除,這里會使用一個集合來存儲所有的頂點,對該集合中的頂點進行刪除動作,集合如下.

List<Integer> toBeChecked = new LinkedList<>();

  

  和上一篇一樣,這里使用一個名為Graph的類來封裝查找最短路徑的相關內容:

/**
 * 使用鄰接矩陣實現圖<p>
 * 深度優先遍歷與廣度優先遍歷<p>
 * 求最短路徑:<p>
 *      1. Dijkstra 算法 <p>
 *      2. Ford 算法 <p>
 *      3. 通用型的糾正標記算法<p>
 * Created by Henvealf on 16-5-22.
 */
public class Graph<T> {
    private int[][] racs;       //鄰接矩陣
    private T[] verticeInfo;   //各個點所攜帶的信息.

    private int verticeNum;             //頂點的數目,
    private int[] visitedCount;         //記錄訪問
    private int[] currDist;             //最短路徑算法中用來記錄每個頂點距離起始頂點路徑的長度.

    public Graph(int[][] racs, T[] verticeInfo){
        if(racs.length != racs[0].length){
            throw new IllegalArgumentException("racs is not a adjacency matrix!");
        }
        if(racs.length != verticeInfo.length ){
            throw new IllegalArgumentException ("Argument of 2 verticeInfo's length is error!");
        }
        this.racs = racs;
        this.verticeInfo = verticeInfo;
        verticeNum = racs.length;
        visitedCount = new int[verticeNum];
    }
    //..........       
}

   這里是使用的鄰接矩陣來表示圖,想要使用其他表示方法,自行稍微修改一下便可.下面是實現方法的代碼:

 1 /**
 2      * 使用 Dijkstra算法尋找最短路徑
 3      * @param first 路徑開始的頂點
 4      * @return 返回最后的最短路徑
 5      */
 6     public int[] dijkstraAlgorithm(int first){
 7         if(first < 0 || first >= verticeNum ){
 8             throw new IndexOutOfBoundsException ("should between 0 ~ " + (verticeNum -1));
 9         }
10         setNumberAsInfinitie();
11         currDist[first] = 0;
12         List<Integer> toBeChecked = new LinkedList<>();
13         for(int i = 0; i < verticeNum; i ++){
14             toBeChecked.add(i);
15         }
16         while(!toBeChecked.isEmpty()){
17             int nowVertice = findMinCurrDistVerticeAndRemoveFormList(toBeChecked);
18             for(int i = 0; i < verticeNum; i ++){
19                 int nextVertice = -1;                       //鄰接節點
20                 int weight = Integer.MAX_VALUE;             //到達鄰接節點的權重
21                 if(racs[nowVertice][i] != Integer.MAX_VALUE){   //得到鄰接頂點
22                     if(toBeChecked.contains(i)){
23                         nextVertice = i;
24                         weight = racs[nowVertice][i];
25                     }
26                 }
27                 if(nextVertice == -1) {continue;}
28                 if(currDist[nextVertice] > currDist[nowVertice] + weight){
29                     currDist[nextVertice] = currDist[nowVertice] + weight;
30                 }
31             }
32 
33         }
34         for(int i = 0; i < currDist.length; i++){
35             System.out.println("現在頂點 " + verticeInfo[i].toString() + " 距離頂點 " + verticeInfo[first].toString()  + " 的最短距離為 " + currDist[i]);
36         }
37         return currDist;
38     }
39   /**
40      * 將currDist數組初始化為無窮大
41      */
42     private void setNumberAsInfinitie(){
43         currDist = new int[verticeNum];
44         for (int i = 0; i < verticeNum; i++){
45             currDist[i] = Integer.MAX_VALUE;
46         }
47     }
48 
49     /**
50      * 尋找出當前距離起始頂點路徑最短的頂點,並將其從toBeCheck中刪除
51      * @param list
52      * @return
53      */
54     private int findMinCurrDistVerticeAndRemoveFormList(List<Integer> list){
55         int num = list.get(0);
56         int dist = currDist[list.get(0)];
57         int listIndex = 0;
58         for(int i = 1; i < list.size(); i ++){
59             int index = list.get(i);
60             if(currDist[index] < dist) {
61                 dist = currDist[index];
62                 num = index;
63                 listIndex = i;
64             }
65         }
66         list.remove(listIndex);
67         return num;
68     }

 

2.Ford 算法 

  上面提到Dijkstra算法不能處理有負權值的情況,所以自然就有替代方法:Ford方法.

  Ford算法並不會像Dijkstra算法一樣去刪除頂點,他時按照一定的順序,來對每個邊進行遍歷並更新設置最短距離.

  比如有一個異常簡單的圖:

    a-->b-->c-->d

  Ford算法要求我們指定邊的遍歷順序,讓每條邊都能夠被走過一次.比如這里我選擇的順序為:b-->c,  a-->b, c-->d.

  算法就會根據指定的該順序,把圖中所有的邊都訪問一次,每訪問完一遍就是一次迭代.在訪問過程中,和Dijkstra算法相似,回進行如下判斷和更新.

if(currDist[now] > currDist[next] + weight){
         currDist[next] = currDist[now] + racs[now][next];
}

 

  然后直到在最后一次迭代中,發現所有的邊都不符合上面的判斷,算法就結束.

  實現代碼如下:

 1 /**
 2      * 使用Ford的方法尋找最短路徑
 3      * @param first 路徑開始的頂點
 4      */
 5     public int[] fordAlgorithm(int first){
 6         if(first < 0 || first >= verticeNum ){
 7             throw new IndexOutOfBoundsException ("should between 0 ~ " + (verticeNum -1));
 8         }
 9         setNumberAsInfinitie();
10         currDist[first] = 0;
11         while(true){
12             boolean hasLessEdge = false;            //是否有使currDist更小的邊
13             for(int s = 0 ; s < verticeNum; s ++){
14                 for (int e = 0; e < verticeNum; e ++){
15                     if(racs[s][e] != Integer.MAX_VALUE){
16                         int weight = getWeightPreventOverflow(s,e);
17                         if(currDist[e] > currDist[s] + weight){
18                             hasLessEdge = true;
19                             currDist[e] = currDist[s] + racs[s][e];
20                         }
21                     }
22                 }
23             }
24             if(!hasLessEdge) { break; }
25         }
26         for(int i = 0; i < currDist.length; i++){
27             System.out.println("現在頂點 " + verticeInfo[i].toString() + " 距離頂點 " + verticeInfo[first].toString()  + " 的最短距離為 " + currDist[i]);
28         }
29 
30         return currDist;
31     }
32 
33     /**
34      * 處理並獲得權重,並且使得到的結果在進行路徑長度的加減操作時不會出現溢出
35      * @param start
36      * @param end
37      * @return
38      */
39     private int getWeightPreventOverflow(int start, int end){
40         int weight = 0;
41         //防止加減法溢出
42         if(currDist[start] == Integer.MAX_VALUE && racs[start][end] > 0){
43             weight = 0;
44         }else if(currDist[start] == Integer.MIN_VALUE && racs[start][end] < 0){
45             weight = 0;
46         }else{
47             weight = racs[start][end];
48         }
49         return weight;
50     }

 

 

 

3.通用型的糾正標記算法

 

未完待續...

by 自安/henvealf 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM