【算法分析與設計】圖:bellman-ford算法

前半部分內容參考自這篇博客

介紹

Bellman-Ford 算法是一種用於計算帶權有向圖中單源最短路徑(SSSP:Single-Source Shortest Path)的算法。該算法由 Richard Bellman 和 Lester Ford 分別發表於 1958 年和 1956 年,而實際上 Edward F. Moore 也在 1957 年發佈了相同的算法,因此,此算法也常被稱爲 Bellman-Ford-Moore 算法。

Bellman-Ford 算法和 Dijkstra 算法同爲解決單源最短路徑的算法。對於帶權有向圖 G = (V, E),Dijkstra 算法要求圖 G 中邊的權值均爲非負,而 Bellman-Ford 算法能適應一般的情況(即存在負權邊的情況)。一個實現的很好的 Dijkstra 算法比 Bellman-Ford 算法的運行時間要低。

Bellman-Ford 算法採用動態規劃(Dynamic Programming)進行設計,實現的時間複雜度爲 O(V*E),其中 V 爲頂點數量,E 爲邊的數量。Dijkstra 算法採用貪心算法(Greedy Algorithm)範式進行設計,普通實現的時間複雜度爲 O(V2),若基於 Fibonacci heap 的最小優先隊列實現版本則時間複雜度爲 O(E + VlogV)。

算法過程

假設圖中的頂點A, B, C, D, E,求頂點A到其他頂點的最短距離。首先初始化A到其他頂點的距離爲無窮大,同時初始化一個待處理頂點庫爲【A】:
距離表:

A B C D E
0

頂點庫:【A】

在這裏插入圖片描述
處理頂點庫中的頂點A:頂點A與B C相鄰,它到另外兩個節點的距離分別是-1,4均小於∞,故更新距離表,並將頂點B、C加入到頂點庫中:
距離表

A B C D E
0 -1 4

頂點庫
【B,C】

處理頂點庫中的頂點B:頂點B與C,D,E相鄰,距離分別爲3,2,2。由於A-B-C這條路徑比A-C這條路徑耗時短,故更新頂點C的距離表,同時更新頂點D和E在距離表中的數據,並將頂點D、E加入到頂點庫:
距離表

A B C D E
0 -1 2 1 1

頂點庫
【C、D、E】

處理頂點庫中的頂點C:發現C沒有通向其他頂點;
更新頂點庫爲:【D、E】

處理頂點庫的頂點D:頂點D通向頂點C,但是對於C來說,經過D的路徑比不經過D的路徑要費時,所以不做處理;
更新頂點庫爲:【E】

處理頂點庫的頂點E:E通向頂點D,且可以將頂點D的路徑成本降低爲-2,故更新距離表,並將具有新路徑成本的頂點D加入到頂點庫中:
路徑表:

A B C D E
0 -1 2 -2 1

頂點庫:
【D】

處理頂點庫中的訂點D:D通向B和C,但是不能將B和C的路徑成本進一步降低了,故路徑表不更新,同時頂點庫中沒有其他頂點了,結束整個過程。

JAVA實現

輸入:
int[][] weights:二維數組,表示起點、終點及對應的路徑權重,例如:

int[][] weights= {
				{2,1,1},
				{2,3,1},
				{3,4,1}
		};

表示下面這個圖:

N:表示節點的個數,上面這個圖的N爲4
K:表示起點

輸出:
int[] path:從節點K出發,到其他所有節點的最短路徑數組。
例如對於上面的圖來說,起點爲節點2時,輸出的path爲:

0 1 2 3 4
0 1 0 1 2
class Solution {
    public int[] bellmanFord(int[][] weights, int N, int K) {
    	// 由路徑權重數組重構這個圖
    	int[][] graph = new int[N+1][N+1];
    	for(int i=0;i<N+1;i++)
    	// graph中的路徑權重爲-1時表示該邊不存在
    	Arrays.fill(graph[i], -1);
        
        for(int[] w: weights) 
        	graph[w[0]][w[1]] =w[2];
      
      // path[i]表示第K到第i個節點時間
      int[] path=new int[N+1];
      // 初始時,默認起點到起點的時間爲0;起點到其他節點的時間爲無窮大
      Arrays.fill(path, Integer.MAX_VALUE);
      path[K] = 0;
      
      // nodes保存等待處理的節點
      Queue<Integer> nodes= new LinkedList<>();
      nodes.offer(K);
      
      while(nodes.size()>0) {
    	  int curNode= nodes.poll();
    	  for(int i=1;i<=N;i++) {
    		  if(i==curNode||graph[curNode][i]==-1)	continue;
    		  int curPath = path[curNode]+graph[curNode][i];
    		  if(curPath<path[i]) {
    			  nodes.offer(i);
    			  path[i] = curPath;
    		  }
    	  }
      }
      return path;
    }
}

例題

leetcode743
解法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章