【数据结构与算法】【算法思想】Dijkstra算法
圖的兩種搜索算法,深度優先搜素和廣度優先搜索。這兩種算法主要是針對無權圖的搜索算法。針對有權圖,也就是圖中的每條邊都有一個權重,該如何計算兩點之間的最短路徑?最短路徑算法(Shortest Path Algorithm)。
一:算法解析
最優問題包含三個:最短路線,最少用時,最少紅綠燈。
1,解訣軟件開發中的實際問題,最重要的一點就是建模,也就是將復雜的場景抽象成具體的數據結構。
2,圖的表達能力強,可以將求解的最短路徑問題轉化為:在一個有向有權圖中,求兩個頂點間的最短路徑。
3,要解決這個問題,有個非常經典的算法,最短路徑算法,更準確的叫:單源最短路徑算法(一個頂點到一個頂點)。提到最短路徑算法,最出名的莫過于DijKstra算法。
Dijkstra算法
算法結構
public class Graph { // 有向有權圖的鄰接表表示private LinkedList<Edge> adj[]; // 鄰接表private int v; // 頂點個數public Graph(int v) {this.v = v;this.adj = new LinkedList[v];for (int i = 0; i < v; ++i) {this.adj[i] = new LinkedList<>();}}public void addEdge(int s, int t, int w) { // 添加一條邊this.adj[s].add(new Edge(s, t, w));}private class Edge {public int sid; // 邊的起始頂點編號public int tid; // 邊的終止頂點編號public int w; // 權重public Edge(int sid, int tid, int w) {this.sid = sid;this.tid = tid;this.w = w;}}// 下面這個類是為了dijkstra實現用的private class Vertex {public int id; // 頂點編號IDpublic int dist; // 從起始頂點到這個頂點的距離public Vertex(int id, int dist) {this.id = id;this.dist = dist;}} }實現算法
// 因為Java提供的優先級隊列,沒有暴露更新數據的接口,所以我們需要重新實現一個 private class PriorityQueue { // 根據vertex.dist構建小頂堆private Vertex[] nodes;private int count;public PriorityQueue(int v) {this.nodes = new Vertex[v+1];this.count = v;}public Vertex poll() { // TODO: 留給讀者實現... }public void add(Vertex vertex) { // TODO: 留給讀者實現...}// 更新結點的值,并且從下往上堆化,重新符合堆的定義。時間復雜度O(logn)。public void update(Vertex vertex) { // TODO: 留給讀者實現...} public boolean isEmpty() { // TODO: 留給讀者實現...} }public void dijkstra(int s, int t) { // 從頂點s到頂點t的最短路徑int[] predecessor = new int[this.v]; // 用來還原最短路徑Vertex[] vertexes = new Vertex[this.v];for (int i = 0; i < this.v; ++i) {vertexes[i] = new Vertex(i, Integer.MAX_VALUE);}PriorityQueue queue = new PriorityQueue(this.v);// 小頂堆boolean[] inqueue = new boolean[this.v]; // 標記是否進入過隊列vertexes[s].dist = 0;queue.add(vertexes[s]);inqueue[s] = true;while (!queue.isEmpty()) {Vertex minVertex= queue.poll(); // 取堆頂元素并刪除if (minVertex.id == t) break; // 最短路徑產生了for (int i = 0; i < adj[minVertex.id].size(); ++i) {Edge e = adj[minVertex.id].get(i); // 取出一條minVetex相連的邊Vertex nextVertex = vertexes[e.tid]; // minVertex-->nextVertexif (minVertex.dist + e.w < nextVertex.dist) { // 更新next的distnextVertex.dist = minVertex.dist + e.w;predecessor[nextVertex.id] = minVertex.id;if (inqueue[nextVertex.id] == true) {queue.update(nextVertex); // 更新隊列中的dist值} else {queue.add(nextVertex);inqueue[nextVertex.id] = true;}}}}// 輸出最短路徑System.out.print(s);print(s, t, predecessor); }private void print(int s, int t, int[] predecessor) {if (s == t) return;print(s, predecessor[t], predecessor);System.out.print("->" + t); }
用 vertexes 數組,記錄從起始頂點到每個頂點的距離(dist)。起初,我們把所有頂點的 dist 都初始化為無窮大(也就是代碼中的Integer.MAX_VALUE)。
把起始頂點的 dist 值初始化為 0,然后將其放到優先級隊列中。我們從優先級隊列中取出 dist 最小的頂點 minVertex,然后考察這個頂點可達的所有頂點(代碼中的 nextVertex)。如果 minVertex 的 dist 值加上 minVertex 與 nextVertex 之間邊的權重 w 小于 nextVertex 當前的 dist 值,也就是說,存在另一條更短的路徑,它經過 minVertex 到達 nextVertex。那我們就把 nextVertex 的 dist 更新為 minVertex 的 dist 值加上 w。然后,我們把 nextVertex 加入到優先級隊列中。重復這個過程,直到找到終止頂點 t 或者隊列為空。
以上就是 Dijkstra 算法的核心邏輯。除此之外,代碼中還有兩個額外的變量,predecessor 數組和 inqueue 數組。
predecessor 數組的作用是為了還原最短路徑,它記錄每個頂點的前驅頂點。最后,我們通過遞歸的方式,將這個路徑打印出來。打印路徑的 print 遞歸代碼我就不詳細講了,這個跟我們在圖的搜索中講的打印路徑方法一樣。如果不理解的話,你可以回過頭去看下那一節。
inqueue 數組是為了避免將一個頂點多次添加到優先級隊列中。我們更新了某個頂點的 dist 值之后,如果這個頂點已經在優先級隊列中了,就不要再將它重復添加進去了。
Dijkstra算法的時間復雜度
O(E*log V),E表示邊的個數,V表示元素個數不會超過頂點的個數
5,Dijkstra最短路徑算法,實際上最短路徑算法還有很多,比如Bellford算法,Floyd算法等。
筆記整理來源: 王爭 數據結構與算法之美
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的【数据结构与算法】【算法思想】Dijkstra算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql触发器 当记录的指定字段发生变
- 下一篇: ICCV 2021可逆的跨空间映射实现多