## :speech_balloon: 介紹 Dijkstra 演算法的目的為找出G(V, E)圖上某一點到另一點的最短路徑,是一種Greedy Algorithm,搭配Binary Heap來尋找最短路徑所需的時間是O(E log(V))。在現實生活中,它則是應用在網路、地圖、交通...。 :::info G = Graph、 V = Verices (節點)、 E = Edges(邊) ::: :::warning 本篇文章從基礎到進階實作皆有,下面是關於建議閱讀章節 * 初學者 * 觀念 * 中階者 * 觀念 * Code Segments (基礎) **參考章節**:Code Segments using STL and min-heap (進階) ::: * 先備知識 * 基本圖論 * 基本資料結構 * C++ program * 文章分類 (參考) * 難度:★★★ * 重要程度:★★ ## :book: 觀念 --- :::warning :bulb: 如果看不太懂虛擬碼可以看演算法解析,先跳過再回來理解虛擬碼 ::: ### ==1. Pseudocode== ### `Dijkstra's algorithm to generate single-source shortest path` **Input:** A directed graph $G=(V,E)$ and a source vertex $v_0$. For each edge $(u,v)\in E$ ,there is a non-negative number $c(u,v)$ associated with it.$|V|=n+1$ <span style="color:green">//輸入為一有向圖 $G=(V,E)$ 以及起始點 $v_0$,對於任意邊 $E$ 皆有一個非負整數 $c$ 關聯(距離),而點的個數有$n+1$個 ( 編號從$0$到$n$ ) </span> **Output:** For each $v\in V$ ,the length of a shortest path from $v_0$ to <span style="color:green">//輸出為起始點$v_0$至任意點$v$之最短路徑長</span> **Pseudocode:** \begin{align} &\color{blue}{\{initial\}}\\\\ &S:=\{v_0\} {\small \color{green}{\;//一開始把v_0加入到S已走訪集合}}\\\\ &For\;i:=1\;to\;n\;do\\\\ &Begin\\\\ &{\small \color{green}{\;//如果v_0與v_i有邊,則將此邊距離暫時作為到v_i所花費的距離,若沒有則代表距離無限}}\\\\ &\;\;\;if\;(v_0,v_i)\in E\;then\\\\ &\;\;\;\;\;\;\;\;\;\;L(v_i):=c(v_0,v_i)\\\\ &\;\;\;else\\\\ &\;\;\;\;\;\;\;\;\;\;L(v_i):=\infty\\\\ &End\\\\ & \color{blue}{\{renew\;distance\;untill\;find\;the\;shortest\;distance\}}\\\\ &For\;i:=1\;to\;n\;do\\\\ &Begin\\\\ &{\small \color{green}{\;//從還未被挑選過的Vertex集合,挑選u點使得到u點的距離最短}}\\\\ &\;\;\;Choose\;u\;from\;V-S\;such\;that\;L(u)\;is\;the\;smallest\\\\ &\;\;\;S:=S\;\cup\;\{u\}\;(^*\;Put\;u\;into\;S^*)\\\\ &{\small \color{green}{\;//更新目前的L(距離表),如果以u點作為中繼站是否有更短的距離?如果是則更新L\;}}\\\\ &\;\;\;For\;all\;w\;in\;V-S\;do\\\\ &\;\;\;\;\;\;\;\;\;\;L(w):=min(L(w),L(u)+c(u,w))\\\\ &End\\\\ \end{align} ### ==2. 演算法解析== ![](https://hackmd.io/_uploads/By6RTzro3.png) ### **Step1. 從v~0~出發與初始化L(距離表)** $S=\{v_0\}$ $V-S=\{v_1,v_2,v_3,v_4,v_5\}$ | v~1~ | v~2~ | v~3~ | v~4~ | v~5~ | | ---- | ---- | ---- | ---- | ---- | | <span style="color:red">1</span> | 6 | ∞ | ∞ | ∞ | ### **Step2. Choose v~1~ 開始更新各點距離** $S=\{v_0,v_1\}$ $V-S=\{v_2,v_3,v_4,v_5\}$ 挑選最短距離邊,故選擇$v_1$為$u$,透過$v_1$到各點距離更新距離表,剩下回合依此類推 | Pass | v~1~ | v~2~ | v~3~ | v~4~ | v~5~ | | --- | ---- | ---- | ---- | ---- | ---- | | **1** | 1 | 6 | ∞ | ∞ | ∞ | | **2** | - | <span style="color:red">4</span> | 5 | 7 | ∞ | ### **Step3. Choose v~2~更新各點距離** $S=\{v_0,v_1,v_2\}$ $V-S=\{v_3,v_4,v_5\}$ | Pass | v~1~ | v~2~ | v~3~ | v~4~ | v~5~ | | ----- | ---- | -------------------------------- | ---- | ---- | ---- | | **1** | 1 | 6 | ∞ | ∞ | ∞ | | **2** | - | 4 | 5 | 7 | ∞ | | **3** | - | - | <span style="color:red">5</span> | 6 | ∞ | ### **Step4. Choose v~3~更新各點距離** $S=\{v_0,v_1,v_2,v_3\}$ $V-S=\{v_4,v_5\}$ | Pass | v~1~ | v~2~ | v~3~ | v~4~ | v~5~ | | ----- | ---- | -------------------------------- | ---- | ---- | ---- | | **1** | 1 | 6 | ∞ | ∞ | ∞ | | **2** | - | 4 | 5 | 7 | ∞ | | **3** | - | - | 5 | 6 | ∞ | | **4** | - | - | - | <span style="color:red">6</span> | 8 | ### **Step5. Choose v~4~更新各點距離** $S=\{v_0,v_1,v_2,v_3,v_4\}$ $V-S=\{v_5\}$ | Pass | v~1~ | v~2~ | v~3~ | v~4~ | v~5~ | | ----- | ---- | -------------------------------- | ---- | ---- | ---- | | **1** | 1 | 6 | ∞ | ∞ | ∞ | | **2** | - | 4 | 5 | 7 | ∞ | | **3** | - | - | 5 | 6 | ∞ | | **4** | - | - | - | 6 | 8 | | **5** | - | - | - | - | <span style="color:red">8</span> | ### **Step5. Choose v~5~結束** $S=\{v_0,v_1,v_2,v_3,v_4,v_5\}$ $V-S=\{\}$ | Pass | v~1~ | v~2~ | v~3~ | v~4~ | v~5~ | | ----- | ---- | -------------------------------- | ---- | ---- | ---- | | **1** | 1 | 6 | ∞ | ∞ | ∞ | | **2** | - | 4 | 5 | 7 | ∞ | | **3** | - | - | 5 | 6 | ∞ | | **4** | - | - | - | 6 | 8 | | **5** | - | - | - | - | 8| | **6** | - | - | - | - | - | ### **Step6. 總結** :::success ### Ans:若從$v_0$出發至各點的最短距離為 | v~1~ | v~2~ | v~3~ | v~4~ | v~5~ | | ---- | ---- | ---- | ---- | ---- | | 1 | 4 | 5 | 6 | 8 | ::: ## :book: Code Segments(基礎) --- ```cpp #include <iostream> #define INF 0x3f3f3f3f #define V_COUNT 6 using namespace std; int adj[V_COUNT][V_COUNT]; void print_answer(int* L){ printf("Vertex Distance from Source\n"); for(int i = 0 ; i < V_COUNT; i++){ printf("%d \t\t %d\n",i,L[i]); } } //初始化相鄰陣列(Adjacency Array)都是沒有邊的情況 void init_adj(){ for(int i = 0 ; i < V_COUNT; i++){ for(int j = 0 ; j < V_COUNT; j++){ adj[i][j]=INF; } } } //加入邊與權重(距離) void addEdge(int u,int v,int w){ adj[u][v] = w; adj[v][u] = w; } //初始化圖 void init_graph(){ init_adj(); addEdge(0, 1, 1); addEdge(0, 2, 6); addEdge(1, 2, 3); addEdge(1, 3, 4); addEdge(1, 4, 6); addEdge(2, 3, 2); addEdge(2, 4, 2); addEdge(3, 5, 3); addEdge(3, 4, 2); addEdge(4, 5, 4); } void initial(int* L_table,int* S){ //初始化 S集合 for(int i=0; i < V_COUNT;i++){ S[i]=0; } //將v0放入S集合 S[0] = 1; //初始化L for(int i = 0 ; i < V_COUNT;i++){ L_table[i]=INF; } L_table[0]=0; //如果v_0與v_i有邊,則將此邊距離暫時作為到v_i所花費的距離,若沒有則代表距離無限 for(int i=1; i < V_COUNT;i++){ if(adj[0][i]!=INF){ L_table[i]=adj[0][i]; } else{ L_table[i]=INF; } } } int choss_u_smallest(int* S,int* L_table){ int min = INF; int flag=0; int vertex; //找最小距離 for(int i = 1 ; i < V_COUNT; i++){ if(S[i]==0 && L_table[i]<min){ flag=1; min = L_table[i]; vertex = i; } } if(flag){ //將挑選的vertex加入到集合s S[vertex]=1; //回傳vertex return vertex; } else{ //V-S集合為空 return -1; } } void renew_distance(int* L,int* S){ int vertex; while(true){ vertex=choss_u_smallest(S,L); if(vertex==-1)break; //更新L表 for(int i = 1 ; i < V_COUNT;i++){ if(i != vertex){ int new_distance = L[vertex]+adj[vertex][i]; if(L[i] > new_distance)L[i]=new_distance; } } } } int main() { int S_set[V_COUNT]; int L_table[V_COUNT]; init_graph(); initial(L_table,S_set); renew_distance(L_table,S_set); print_answer(L_table); return 0; } ``` ![](https://hackmd.io/_uploads/ry0BcHSon.png) ## :book: Code Segments using STL and min-heap (進階) --- ```cpp= #include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f typedef pair<int,int> iPair; //類別 class Graph{ int V; //有多少個點(Vertices) list<pair<int,int>>* adj; //用相鄰串列(Adjacency List)記錄圖(Graph) public: Graph(int V);//建構子 //添加E(邊)與權重,其中E(邊)是由兩個點(vertices)構成 void addEdge(int u,int v,int w); //印出最短路徑 void shortestPath(int s); }; //類別實作 Graph::Graph(int V){ this->V = V;//有多少個點(Vertices) adj = new list<iPair>[V];//創建相鄰串列(Adjacency List),有V個點就有V個串列 } void Graph::addEdge(int u,int v,int w){ adj[u].push_back(make_pair(v,w));//以u為主的list儲存點v與到點v的權重(w) adj[v].push_back(make_pair(u,w));//v也可以到u } //印出點src到所有點的距離 void Graph::shortestPath(int src){ //創建priority queue用於儲存vertices與距離,快速決定該回合最小距離 //對於priority_queue的用法可以查看下面連結教學 // https://www.geeksforgeeks.org/implement-min-heap-using-stl/ priority_queue<iPair, vector<iPair>,greater<iPair>> pq; //設定到所有點的距離為無限(INF) //vector可以想成進階版array,下面的宣告代表 //dist[0]=INF,dist[1]=INF,...,dist[V-1]=INF vector<int> dist(V, INF); //將起始點放入priority queue並初始化 //make_pair(distance,vertex) //將起始點設定為距離0 pq.push(make_pair(0,src)); dist[src]=0; //持續loop直到priority queue為空 while(!pq.empty()){ //第一個vertex一定是起始點且他距離是0 //pair的內容是(distance,vertex) //pair的第一項內容是距離(distance or weight) //pair的第二項內容是點(vertex) int u = pq.top().second;//取出vertex pq.pop();//取出後從pq移除 //利用迭代器去走訪list list<pair<int,int>>::iterator i; //走訪當前vertex u的相鄰串列(走訪目前u能到的點) for(i = adj[u].begin();i!=adj[u].end();++i){ int v = (*i).first;//u能到v點 int weight = (*i).second;//u到v點的距離 //如果從(起始點 ->u -> v)的距離小於目前所記錄 (起始點-> v) 的距離 //代表找到更短的路徑,更新目前到該點最短距離 //並將(distance,vertex)加入priority queue,等到迴圈結束再來評估下一步要前往哪個vertex(挑最小距離走) if(dist[v] > dist[u]+weight){ dist[v] = dist[u]+weight; pq.push(make_pair(dist[v],v)); } } } //迴圈結束後代表已經記錄完所有點的距離 //印出 printf("Vertex Distance from Source\n"); for(int i = 0; i < V; ++i){ printf("%d \t\t %d\n",i,dist[i]); } } int main() { int V = 6;//有6個點 Graph g(V);//創建圖 //為點加上邊 g.addEdge(0, 1, 1); g.addEdge(0, 2, 6); g.addEdge(1, 2, 3); g.addEdge(1, 3, 4); g.addEdge(1, 4, 6); g.addEdge(2, 3, 2); g.addEdge(2, 4, 2); g.addEdge(3, 5, 3); g.addEdge(3, 4, 2); g.addEdge(4, 5, 4); //從0開始走訪並印出 g.shortestPath(0); return 0; } ``` ![](https://hackmd.io/_uploads/SJ8PqBHj3.png) ## :link: 參考 --- * [How to find Shortest Paths from Source to all Vertices using Dijkstra’s Algorithm](https://www.geeksforgeeks.org/dijkstras-shortest-path-algorithm-greedy-algo-7/) * Introduction to the Design and Analysis of Algorithms (A Strategic Approach) R.C.T. Lee, S.S. Tseng, R.C. Chang, and Y.T. Tsai ###### tags: `資料結構` `Algorithm`