貪婪算法-單源最短路徑

前言

感謝每一位朋友的閱讀與建議,今天對最短路徑blog進行了修改,調整圖和部份內容。感謝各位關注。提前祝你們聖誕節平安快樂。java

單源最短路徑問題描述

給定一個帶權有向圖G=(V,E),其中每條邊的權是一個實數。另外,還給定V中的一個頂點,稱爲源。如今要計算從源到其餘全部各頂點的最短路徑長度。這裏的長度就是指路上各邊權之和。這個問題一般稱爲單源最短路徑問題git

1.無權最短路徑(非惟一)

算法分析

  1. 因爲圖沒有權,因此咱們只須要關注路徑上的邊
  2. 無權最短路徑實質上是特殊的有權最短路徑,由於咱們能夠將每條邊按權爲1處理。
  3. 咱們能夠一層一層處理,先找與s距離爲1的節點,以後找距離爲2的節點,直到全部節點都被訪問到。 注: 按層搜索圖的方式,稱爲廣度優先搜索,這種搜索方式相似樹的層序遍歷。

算法描述

藉助隊列實現每條邊只訪問一次。github

  1. 初始狀況下聲明全部節點的最短路徑未知
  2. 起點s聲明最短路徑爲0,並將s入隊。
  3. 從隊列中移除一個節點v ,並更新該點v的臨接表wlist中每個臨接點w的最短路徑爲當前最短路徑dv+1
  4. 重複1-3步驟 ,直到隊列爲空爲止。

圖解說明

無權最短路徑

核心代碼

/**
  * 無權最短路徑
  * 
  * @param s 起點
  */
public void unweight(Vertex s) {
	Queue<Vertex> q = new LinkedList<Vertex>();
	for (Vertex x : graph) {
	 //每一個節點的初始最短路徑爲Integer的最大值,表示該節點的最短路徑未知
	  x.setDist(Integer.MAX_VALUE);
	}
	s.dist = 0;
	q.add(s);
	while (!q.isEmpty()) {
		Vertex v = q.poll();
	  if (v != null) {
		if (v.getAdj() != null && !v.getAdj().isEmpty()) {
		  for (Vertex w : v.getAdj()) {
			if (w.getDist() == Integer.MAX_VALUE) {//每條邊只訪問一次
				w.setDist(v.getDist()+1);
				w.setPath(v);
				q.add(w);
			}
		  }
	    }
     }
   }
}

該算法的時間界限

O(|E|+|V|)

2.有權無負值最短路徑

Dijkstra算法是解決有權無負值單源最短路徑的經典算法。算法

Dijkstra算法描述

  1. 選擇一個未知最短路徑的節點v,它在全部未知最短路徑的節點集中有最小的路徑dv。
  2. 聲明v爲已知最短路徑節點
  3. 更新v的臨接頂點集,針對每一個v的臨接點w,若dv+cvw<dw,則更新w的路徑。
    注:cvw爲邊(v,w)的權,dv,dw分別爲v,w的最短路徑
  4. 重複1-3步驟,直到全部頂點的最短路徑都已知。

圖解說明

Dijkstra算法1

Dijkstra算法2

核心代碼

/**
	 * 著名的dijkstra算法 解決單源最短路徑(權無負值)
	 * 
	 * @param s
	 *            起點
	 */
public void dijkstra(Vertex s) {
	for (Vertex v : graph) {// 初始默認全部頂點未被訪問
		v.setDist(Integer.MAX_VALUE);
		v.known = false;
	}
	s.dist = 0;// 聲明起點的距離爲0
	PriorityQueue<Vertex> priorityQueue = new PriorityQueue<Vertex>();
	// 將s放入優先隊列
	priorityQueue.add(s);
	while (!priorityQueue.isEmpty()) {// 知道全部頂點的最短路徑都已知而且優先隊列爲空
		Vertex v = priorityQueue.poll();// 取出未知節點中最短路徑最小的節點
		if (v != null) {
			if (!v.known) {
				v.known = true;// 聲明該節點已知
				if (v.getAdj() != null && !v.getAdj().isEmpty()) {
				// 如 dv+cvw<=dw 則更新臨接點的最短路徑,而且更新值放入到優先隊列中
				for (AdjVertex adjW : v.getAdj()) {
				if (!adjW.getW().known) {
					if (v.dist + adjW.cvw < adjW.getW().getDist()) {
					  adjW.getW().setDist(v.dist + adjW.cvw);
					  adjW.getW().setPath(v);
					  priorityQueue.add(adjW.getW());
								}
							}
						}
					}
				}
			}
		}
	}

3.有權有負值無圈最短路徑(非惟一)

問題定義

針對一個有權圖,該圖的權有負值,使用某個頂點s做爲輸入參數,找出該頂點s到其餘頂點的最短距離。url

爲什麼不能使用Dijkstra算法

  1. Dijkstra有可能過早的聲明一個節點的最短路徑已知,因爲有權有負值存在,可能還有一條含有負值邊的路徑通過該節點,使得該節點的最短路徑更小。
  2. 利用一個修正值,將圖的邊修正爲正數以後使用Dijkstra,也是不行的 ,由於含有較多邊的路徑會被過分修正,以下圖所示:

圖解說明「修正負值出現的問題」

修正負值出現的問題1

有權有負值無圈最短路徑的解法

藉助廣度優先搜素實現。.net

  1. 將起點放入隊列
  2. 從隊列中取出節點v,更新v的臨接頂點集,針對每一個v的臨接點w,若dv+cvw<dw,則更新w的路徑。
    注:cvw爲邊(v,w)的權,dv,dw分別爲v,w的最短路徑
  3. 當w不在隊列中時,將w放入隊列
  4. 直到隊列爲空爲止

核心代碼

/**
	 * 有權有負值最短路徑
	 * 藉助廣度優先搜素
	 * @param s 起點
	 */
	public void weightNegative(Vertex s) {
		Queue<Vertex> q = new LinkedList<Vertex>();
		for (Vertex v : graph) {
			v.dist = Integer.MAX_VALUE;
			v.isInQueue = false;
		}
		s.dist = 0;
		s.isInQueue = true;
		q.add(s);
		while (!q.isEmpty()) {
			Vertex v = q.poll();
			v.isInQueue = false;
			if (v.getAdj() != null && !v.getAdj().isEmpty()) {
				for (AdjVertex wadj : v.getAdj()) {
					if (wadj.cvw + v.dist < wadj.getW().dist) {
						wadj.getW().setDist(wadj.cvw + v.dist);
						wadj.getW().setPath(v);
						if (!wadj.getW().isInQueue) {
							wadj.getW().isInQueue = false;
							q.add(wadj.getW());
						}
					}
				}
			}
		}
	}

時間界限

O(|E|*|V|)

總結與補充

  1. 上述算法,若圖有圈,都不能執行,上述算法是以臨接表的方式標示圖的。
  2. 無權最短路徑藉助廣度優先搜素實現,其時間界限爲:
O(|E|+|V|)
  1. Dijkstra是解決有權無負值圖單源最短路徑的經典算法。3d

  2. 若權有負值,藉助廣度優先搜素與有權無負值最短路徑思想結合來解決 ,其時間界限爲:code

O(|E|*|V|)

完整代碼地址

碼雲地址

無權無圈最短路徑blog

有權無負值最短路徑隊列

有權有負值最短路徑

github地址

無權無圈最短路徑

有權無負值最短路徑

有權有負值最短路徑

相關文章
相關標籤/搜索