圖的最短路徑(Dijkstra | Floyd)

sabot_v發表於2020-11-19
1. Dijkstra演算法

從起始點開始,採用貪心演算法的策略,每次遍歷到始點距離最近且未訪問過的頂點的鄰接節點,直到擴充套件到終點為止。


在這裡插入圖片描述


最短距離是指從1節點到當前節點的最短距離。演算法的複雜度為O(n^2)。對此,可以考慮用堆這種資料結構進行優化,取出最短路徑的複雜度降為O(1);每次調整的複雜度降為O(elogn);e為該點的邊數,所以複雜度降為O((m+n)logn)。

實現:

  1. 將源點加入堆,並調整堆。
  2. 選出堆頂元素u(即代價最小的元素),從堆中刪除,並對堆進行調整。
  3. 處理與u相鄰的,未被訪問過的,滿足三角不等式的頂點
    1):若該點在堆裡,更新距離,並調整該元素在堆中的位置。
    2):若該點不在堆裡,加入堆,更新堆。 [4]
  4. 若取到的u為終點,結束演算法;否則重複步驟2、3。
//假設起點為src, 終點為dst, 圖以二維矩陣的形式儲存,若graph[i][j] == 0, 代表i,j不相連    
//visit[i] == 0,代表未訪問,visit[0] == -1代表已訪問    
public int Dijkstra(int src, int dst, int[][] graph,int[] visit){
    //節點個數        
    int n = graph.length;        
    PriorityQueue<Node> pq = new PriorityQueue<>(new Node());        
    //將起點加入pq        
    pq.add(new Node(src, 0));        
    while (!pq.isEmpty()){            
        Node t = pq.poll();            
        //當前節點是終點,即可返回最短路徑            
        if(t.node == dst)                
            return t.cost;            
        //t節點表示還未訪問            
        if (visit[t.node]==0){                
            //將節點設定為已訪問                
            visit[t.node] = -1;                
            //將當前節點相連且未訪問的節點遍歷                
            for (int i = 0; i < n; i++) {                    
                if (graph[t.node][i]!=0 && visit[i]==0) {                        
                    pq.add(new Node(i, t.cost + graph[t.node][i]));                    
                }                
            }            
        }        
    }        
    return -1;    
}    
//定義一個儲存節點和離起點相應距離的資料結構    
class Node implements Comparator<Node> {        
    public int node;        
    public int cost;
             
    public Node(){}
     
    public Node(int node, int cost){            
        this.node = node;            
        this.cost = cost;        
    }        
    @Override        
    public int compare(Node node1, Node node2){            
        return node1.cost-node2.cost;       
    }    
}
2. Floyd演算法

弗洛伊德是求解圖中任意兩點間最短路徑的演算法。

演算法時間複雜度與用迪傑斯特拉求解相同,但是弗洛伊德演算法十分簡單,核心程式碼只需三行。

演算法思想為:

1)任意兩點間的初始最短距離,為以兩點為端點的邊的權重。

2)遍歷所有點(k),若任意兩點(i和j),滿足(i,k)最短距離 + (k,j)最短距離 < (i,j)最短距離,則取(i,j)最短距離而代之。

for(k=1;k<=n;k++)  
    for(i=1;i<=n;i++)  
        for(j=1;j<=n;j++)  
            if(e[i][j]>e[i][k]+e[k][j])  
                e[i][j]=e[i][k]+e[k][j];  

參考連結:https://www.jianshu.com/p/959e270cb850

相關文章