有向無環帶權圖的最短路徑及長度


  給定一個有向無環圖的拓撲序列,獲取這個序列從起點到序列最后一點的最短路徑。

  起點默認為0點(頂點為0,1,2。。。和數組索引對應),序列通過拓撲排序獲取。

  下面給出實現,首先是對一個有向無環圖進行拓撲排序的類。

package graphics.dag.topologicalsort;

/**
 * 獲取一個拓撲序列
 * @author zhangxinren
 *
 */
public class TopologicalSort {
    // 每條邊的入度
    private static int[] inDegree;// 鄰接表元素個變化,inDegree初始長度也變化
    // 當前可以走的邊(入度為0的邊)
    private static LinkedNode next;

    public static int[] topologicalSort(int[][] edges) {
        int m = 0;
        int[] result = new int[edges.length];
        inDegree = new int[edges.length];

        for (int i = 0; i < inDegree.length; i++) {
            inDegree[i] = 0;
        }

        for (int i = 0; i < edges.length; i++) {
            for (int j = 0; j < edges[i].length; j++) {
                inDegree[edges[i][j]]++;
            }
        }

        for (int i = 0; i < inDegree.length; i++) {
            if (inDegree[i] == 0) {
                if(next == null){
                    next = new LinkedNode(i);
                }
                else
                {
                    LinkedNode tempNode = new LinkedNode(i);
                    tempNode.next = next.next;
                    next.next = tempNode;
                }
            }
        }
                
        while (next != null) {// 沒有入度為零的頂點時結束
            LinkedNode temp = next.next;// 取出一個入度為零的頂點
            if(temp != null){
                next.next = temp.next;
            }
            else{
                temp = next;
                next = null;
            }
            
            result[m++] = temp.number;
            
            int[] tempDegree = edges[temp.number];
            
            for(int i = 0; i < tempDegree.length; i++){// 更新頂點入度和入度為0的點
                inDegree[tempDegree[i]]--;
                if(inDegree[tempDegree[i]] == 0){
                    LinkedNode tempNode = new LinkedNode(tempDegree[i]);
                    if(null != next){
                        tempNode.next = next.next;
                        next.next = tempNode;
                    }
                    else
                    {
                        next = tempNode;
                    }
                }
            }
        }
        
        return result;
    }
}

    輔助的鏈表類

package graphics.dag.topologicalsort;

public class LinkedNode {
    int number;
    LinkedNode next;
    public LinkedNode(int number) {
        super();
        this.number = number;
    }
}

 加上一個獲取最短路徑及最短路徑長度的類,類中由起點0到各頂點的最短路徑長度及最短路徑都可以獲取,讀者也可以修改起點,獲得不同起點到其它點的最短路徑。

package graphics.dag.topologicalsort;

/**
 * 有向無環帶權圖最短路徑
 * @author zhangxinren
 *
 */
public class ShortestPathLength {
    // 到頂點的最短路徑數組
    private static int[] shortest;
    // 前一個結點到當前結點路徑最短時的前一個結點
    private static int[] pred;
    // 頂點鄰接表
    private static int[][] edges = {
            {1,2,3},{4},{4,5},{5},{6},{6},{}
    };
    // 邊權值的鄰接矩陣
    private static int[][] weight = {
            {Integer.MAX_VALUE, 1, 5, 6, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE},
            {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 7, Integer.MAX_VALUE, Integer.MAX_VALUE},
            {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 2, 5, Integer.MAX_VALUE},
            {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 8, Integer.MAX_VALUE},
            {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 4},
            {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 3},
            {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE}
    };
    
    public static int shortestPathLength(int[][] edges, int[][] weight){
        int n = edges.length;
        shortest = new int[n];
        pred = new int[n];
        
        // 初始化:認為除起點0的最短路徑為0外其它都為無窮大,當前頂點的最短路徑的前一個頂點為無(-1)
        for(int i = 0; i < n; i++){
            shortest[i] = Integer.MAX_VALUE;
            pred[i] = -1;
        }
        
        shortest[0] = 0;
        
        // 獲取一個拓撲序列
        int[] sequence = TopologicalSort.topologicalSort(edges);
        
        // 處理拓撲序列中的每一個頂點
        for(int i = 0; i < sequence.length; i++){
            int temp = sequence[i];
            // 獲取當前頂點為出來邊的頂點
            int[] tempDegree = edges[temp];
            
            // 更新這些頂點的最短距離
            for(int j = 0; j < tempDegree.length; j++){
                int end = tempDegree[j];
                relax(temp, end);
            }
        }
        
        return shortest[sequence[sequence.length - 1]];
    }
    
    /**
     * start頂點到它的下一個頂點end,看是否需要更新shortest[end]
     * 在到start頂點最短距離加上start與end的距離小於到end頂點最短距離時,更新最短距離
     * @param start
     * @param end
     * @return
     */
    public static boolean relax(int start, int end){
        if(shortest[start] != Integer.MAX_VALUE && shortest[end] > shortest[start] + weight[start][end]){
            shortest[end] = shortest[start] + weight[start][end];
            pred[end] = start;
            return true;
        }
        
        return false;
    }
    
    public static void main(String[] args) {
        // 獲取最短路徑長度
        int result = shortestPathLength(edges, weight);
        
        int[] sequence = TopologicalSort.topologicalSort(edges);
        System.out.print("sequence: ");
        for(int i = 0; i < sequence.length; i++){
            System.out.print(sequence[i] + " ");
        }
        System.out.println();
        int end = sequence[sequence.length - 1];
        System.out.println("result: " + result);

        StringBuilder sb = new StringBuilder(end + " ");
        int pre = pred[end];
        while(pre != -1){
            sb.append(pre + " ");
            pre = pred[pre];
        }
        sb.setLength(sb.length() - 1);
        sb.reverse();
        
        // 打印出最短路徑
        System.out.println(sb.toString());
        
        // 打印出到所有點的最短路徑長度
        System.out.println("從0開始的最短路徑");
        for(int i = 0; i < edges.length; i++){
            System.out.println(i + ": " + shortest[i]);
        }
    }
}

  下面附上有向無環帶權圖

  圖中基本算法來源於算法基礎-打開算法之門一書,根據書中描述加上本人理解加工以代碼形式加以實現。理解能力有限,如果看不太懂,可以查看相關資料或者找到書籍自行查看。

  最后打印的結果如下:

sequence: 0 3 2 5 1 4 6 
result: 11
0 2 4 6
從0開始的最短路徑
0: 0
1: 1
2: 5
3: 6
4: 7
5: 10
6: 11


免責聲明!

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



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