最短路徑算法—Dijkstra算法詳解

介紹

對於dijkstra算法,不少人可能感受熟悉而又陌生,可能大部分人比較瞭解bfs和dfs,而對dijkstra和floyd算法可能知道大概是圖論中的某個算法,可是可能不清楚其中的做用和原理,又或許,你曾經感受它很難,那麼,這個時候正適合你從新認識它。java

Dijkstra能是幹啥的? node

在這裏插入圖片描述

Dijkstra是用來求單源最短路徑的算法

就拿上圖來講,假如直到的路徑和長度已知,那麼可使用dijkstra算法計算南京到圖中全部節點的最短距離。數組

單源什麼意思?this

  • 從一個頂點出發,Dijkstra算法只能求一個頂點到其餘點的最短距離而不能任意兩點。

bfs求的最短路徑有什麼區別?spa

  • bfs求的與其說是路徑,不如說是次數。由於bfs他是按照隊列一次一次進行加入相鄰的點,而兩點之間沒有權值或者權值相等(代價相同)。處理的更可能是偏向迷宮類的這種都是隻能走鄰居(不排除特例)。

Dijkstra在處理具體實例的應用仍是不少的,由於具體的問題其實帶權更多一些。3d

好比一個城市有多個鄉鎮,鄉鎮可能有道路,也可能沒有,整個鄉鎮聯通,若是想計算每一個鄉鎮到a鎮的最短路徑,那麼Dijkstra就派上了用場。code

算法分析

對於一個算法,首先要理解它的運行流程。 對於一個Dijkstra算法而言,前提是它的前提條件和環境:cdn

  • 一個連通圖,若干節點,節點可能有數值,可是路徑必定有權值。而且路徑不能爲負。不然Dijkstra就不適用。

Dijkstra的核心思想是貪心算法的思想。不懂貪心?blog

貪心算法(又稱貪婪算法)是指,在對問題求解時,老是作出在當前看來是最好的選擇。也就是說,不從總體最優上加以考慮,他所作出的是在某種意義上的局部最優解。 貪心算法不是對全部問題都能獲得總體最優解,關鍵是貪心策略的選擇,選擇的貪心策略必須具有無後效性,即某個狀態之前的過程不會影響之後的狀態,只與當前狀態有關。

對於貪心算法,在不少狀況都能用到。下面舉幾個不恰當的例子!

打個比方,吃自助餐,目標是吃回本,那麼胃有限那麼每次都僅最貴的吃。

上學時,麻麻說只能帶5個蘋果,你想帶最多,那麼選五個蘋果你每次都選最大的那個五次下來你就選的最重的那個。

不難發現上面的策略雖然沒有很強的理論數學依據,或者不太好說明。可是事實規律就是那樣,而且對於貪心問題大部分都須要排序,還可能會遇到類排序。而且一個物體可能有多個屬性,不一樣問題須要按照不一樣屬性進行排序,操做。

那麼咱們的Dijkstra是如何貪心的呢?對於一個點,求圖中全部點的最短路徑,若是沒有正確的方法胡亂想確實很難算出來,而且若是暴力匹配複雜度呈指數級上升不適合解決實際問題。

那麼咱們該怎麼想呢?

Dijkstra算法的前提

  1. 首先,Dijkstra處理的是帶正權值的有權圖,那麼,就須要一個二維數組(若是空間大用list數組)存儲各個點到達()的權值大小。(鄰接矩陣或者鄰接表存儲)
  2. 其次,還須要一個boolean數組判斷那些點已經肯定最短長度,那些點沒有肯定。int數組記錄距離(在算法執行過程可能被屢次更新)。
  3. 須要優先隊列加入已經肯定點的周圍點。每次拋出肯定最短路徑的那個而且肯定最短,直到全部點路徑肯定最短爲止。

簡單的歸納流程爲

  • 通常從選定點開始拋入優先隊列。(路徑通常爲0),boolean數組標記0的位置(最短爲0) , 而後0周圍連通的點拋入優先隊列中(多是node類),並把各個點的距離記錄到對應數組內(若是小於就更新,大於就不動,初始第一次是無窮確定會更新),第一次就結束了
  • 從隊列中拋出距離最近的那個點B第一次就是0周圍鄰居)。這個點距離必定是最近的(全部權值都是正的,點的距離只能愈來愈長。)標記這個點爲true而且將這個點的鄰居加入隊列(下一次肯定的最短點在前面未肯定和這個點鄰居中產生),並更新經過B點計算各個位置的長度,若是小於則更新!
    在這裏插入圖片描述
  • 重複二的操做,直到全部點都肯定。
    在這裏插入圖片描述

算法實現

package 圖論;

import java.util.ArrayDeque;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;

public class dijkstra {
	static class node {
		int x; //節點編號
		int lenth;//長度
		public node(int x,int lenth) {
			this.x=x;
			this.lenth=lenth;
		}
	}

	public static void main(String[] args) {
		 
		int[][] map = new int[6][6];//記錄權值,順便記錄連接狀況,能夠考慮附加鄰接表
		initmap(map);//初始化
		boolean bool[]=new boolean[6];//判斷是否已經肯定
		int len[]=new int[6];//長度
		for(int i=0;i<6;i++)
		{
			len[i]=Integer.MAX_VALUE;
		}
		Queue<node>q1=new PriorityQueue<node>(com);
		len[0]=0;//從0這個點開始
		q1.add(new node(0, 0));
		int count=0;//計算執行了幾回dijkstra
		while (!q1.isEmpty()) {
			node t1=q1.poll();
			int index=t1.x;//節點編號
			int length=t1.lenth;//節點當前點距離
			bool[index]=true;//拋出的點肯定
			count++;//其實執行了6次就能夠肯定就不須要繼續執行了 這句無關緊要,有了減小計算次數
			for(int i=0;i<map[index].length;i++)
			{
				if(map[index][i]>0&&!bool[i])
				{
					node node=new node(i, length+map[index][i]);
					if(len[i]>node.lenth)//須要更新節點的時候更新節點並加入隊列
					{
						len[i]=node.lenth;
						q1.add(node);
					}
				}
			}
		}		
		for(int i=0;i<6;i++)
		{
			System.out.println(len[i]);
		}
	}
	static Comparator<node>com=new Comparator<node>() {

		public int compare(node o1, node o2) {
			return o1.lenth-o2.lenth;
		}
	};

	private static void initmap(int[][] map) {
		map[0][1]=2;map[0][2]=3;map[0][3]=6;
		map[1][0]=2;map[1][4]=4;map[1][5]=6;
		map[2][0]=3;map[2][3]=2;
		map[3][0]=6;map[3][2]=2;map[3][4]=1;map[3][5]=3;
		map[4][1]=4;map[4][3]=1;
		map[5][1]=6;map[5][3]=3;	
	}
}

複製代碼

執行結果:

在這裏插入圖片描述
固然,dijkstra算法比較靈活,實現方式也可能有點區別,可是思想是不變的:一個貪心思路。dijkstra執行一次就可以肯定一個點,因此只須要執行點的總和次數便可完成整個算法。

歡迎感謝小夥伴點贊、關注,贈人玫瑰,手有餘香!蟹蟹!

相關文章
相關標籤/搜索