<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Dijkstra(迪傑斯特拉)演演算法是典型的單源最短路徑演演算法,用於計算一個節點到其他所有節點的最短路徑。主要特點是以起始點為中心向外層層擴充套件,直到擴充套件到終點為止。Dijkstra演演算法是很有代表性的最短路徑演演算法,在很多專業課程中都作為基本內容有詳細的介紹,如資料結構,圖論,運籌學等等。注意該演演算法要求圖中不存在負權邊。對應問題:在無向圖G=(V,E)中,假設每條邊E(i)的長度W(i),求由頂點V0到各節點的最短路徑。
Dijkstra演演算法將頂點集合分為兩組,一組記錄已經求得最短路徑的頂點記為finallyNodes,一組正在求解中的頂點記為processNodes,step1:finallyNodes中頂點最開始只有源節點,最短路徑長度為0,而processNodes中包含除源節點以外的節點,並初始化路徑長度,與源節點直接相連的記路徑長度為權重,不相連的記為∞。step2:從process中選擇路徑長度最小的頂點,加入finallyNodes,並且更新processNodes,將與當前頂點相連的頂點路徑長度更新為min(當前權重,當前頂點最短路徑長度+當前頂點與頂點相連邊權重)。step3:重複step2,直至processNodes陣列為空。
這次我想先描述一下自己的大概思路,下面再寫具體實現。首先為了方便,我採用的是鄰接表儲存圖結構,鄰接表是一個二維陣列,值儲存權重。根據上面工作過程中描述的內容,我們會有兩個中間集合記錄,finallyNodes記錄的是最終結果,我們只需要將計算的結果往裡面塞即可。但是processNodes卻是一個不斷變化更新的集合,其中的操作包括刪除節點,更改節點值,查詢節點值,同時我們每次需要拿出processNodes中記錄的距離最小的值,所以ProcessNodes準備用最小堆來做,那再刪除節點,更改節點值之後都需要調整堆為最小堆,java自帶的優先佇列沒有提供更改節點值的操作,因此我們這裡需要自己實現一個小根堆,支援以上操作。然後就中規中矩實現dijkstra演演算法即可。
如果對堆不太熟悉的可以先看看這篇文章:堆(優先佇列),這裡就不過多解釋了,直接貼程式碼。這裡堆中存的資料格式為int二維陣列,儲存節點下標位置和對應距離,排序按儲存的距離進行排序。
public class MinHeap { List<int[][]> heap ; /** * 獲取並移除堆頂元素,並調整堆 * @return */ public int[][] pop() { int[][] top = heap.get(0); heap.set(0, heap.get(heap.size() - 1)); heap.remove(heap.size() - 1); //調整堆 this.adjust(0, heap.size() - 1); return top; } /** * 判斷是否為空 * @return true/false */ public boolean isEmpty() { if (null == this.heap) { return true; } if (this.heap.size() == 0) { return true; } return false; } /** * 修改index位置節點的value值,並調整最小堆(Java priorityQueue未提供) * @param index 修改節點位置 * @param value 修改值 */ public void changeValue(int index, int value) { int src = heap.get(index)[0][1]; heap.get(index)[0][1] = value; //直接比較當前值是變大還是變小,然後考慮是向上調整還是向下調整 //則當前值可能往上移動 if (src > value) { this.upAdjust(index); return; } this.adjust(index, heap.size() - 1); } public void upAdjust(int index) { //依次與雙親節點進行比較,小於雙親節點就直接交換。一直到根節點 while (index > 0) { int parent = index >> 1; //雙親節點本來小於當前節點不需要進行調整 if (heap.get(parent)[0][1] <= heap.get(index)[0][1]) { break; } swap(index, parent); index = parent; } } /** * 初始化一個最小堆 * @param nums */ public void init(int[][] nums) { heap = new ArrayList<>(nums.length); for (int i = 0 ; i < nums.length; i ++) { int[][] temp = new int[1][2]; temp[0][0] = nums[i][0]; temp[0][1] = nums[i][1]; heap.add(temp); } //從最後一個雙親節點開始將堆進行調整 for (int i = nums.length / 2 ; i >= 0 ; -- i) { this.adjust(i, nums.length - 1); } } /** * 從當前index開始調節為最小堆 * @param index 當前節點下標 * @param end 最後一個節點下標 */ private void adjust(int index, int end) { //找到當前節點的孩子節點,將較小的節點與當前節點交換,一直往下,直至end while (index <= end) { //左孩子節點 int left = index << 1; if (left + 1 <= end && heap.get(left + 1)[0][1] < heap.get(left)[0][1] ) { //找到當前較小的節點 ++ left; } //沒有孩子節點,或者當前的孩子節點均已大於當前節點,已符合最小堆,不需要進行調整 if (left > end || heap.get(index)[0][1] <= heap.get(left)[0][1]) { break; } swap(index, left); index = left; } } private void swap(int i, int j) { int[][] temp = heap.get(i); heap.set(i, heap.get(j)); heap.set(j, temp); } }
資料結構
圖節點僅儲存節點值,一個Node陣列nodes,儲存圖中所有節點,一個二維陣列adjacencyMatrix,儲存圖中節點之間邊的權重,行和列下標與nodes陣列下標對應。
//節點 Node[] nodes; //鄰接矩陣 int[][] adjacencyMatrix; public class Node { private char value; Node(char value) { this.value = value; } }
初始化
初始化圖values標誌的圖中所有節點值,edges標誌圖中邊,資料格式為(node1的下標,node2的下標,邊權重)
private void initGraph(char[] values, String[] edges) { nodes = new Node[values.length]; //初始化node節點 for (int i = 0 ; i < values.length ; i ++) { nodes[i] = new Node(values[i]); } adjacencyMatrix = new int[values.length][values.length]; //初始化鄰接表,同一個節點權重記為0,不相鄰節點權重記為Integer.MAX_VALUE for (int i = 0 ; i < values.length ; i++) { for (int j = 0 ; j < values.length ; j ++) { if (i == j) { adjacencyMatrix[i][j] = 0; continue; } adjacencyMatrix[i][j] = Integer.MAX_VALUE; adjacencyMatrix[j][i] = Integer.MAX_VALUE; } } //根據edges更新相鄰節點權重值 for (String edge : edges) { String[] node = edge.split(","); int i = Integer.valueOf(node[0]); int j = Integer.valueOf(node[1]); int weight = Integer.valueOf(node[2]); adjacencyMatrix[i][j] = weight; adjacencyMatrix[j][i] = weight; } visited = new boolean[nodes.length]; }
初始化dijsktra演演算法必要的finallyNodes和processNodes
/** * 標誌對應下標節點是否已經處理,避免二次處理 */ boolean[] visited; /** * 記錄已經求得的最短路徑 finallyNodes[0][0]記錄node下標,finallyNodes[0][1]記錄最短路徑長度 */ List<int[][]> finallyNodes; /** * 記錄求解過程目前的路徑長度,因為每次取當前已知最短,所以最小堆進行記錄 * 但是java優先佇列沒有實現改變值,這裡需要自己實現 * 首先每次取出堆頂元素之後,堆頂元素加入finallyNodes,此時需要更新與當前元素相鄰節點的路徑長度 * 然後重新調整小根堆 * 首先:只會更新變小的資料,所以從變小元素開始往上進行調整,或者直接呼叫調整方法,從堆頂往下進行調整 */ MinHeap processNodes; /** * 初始化,主要初始化finallyNodes和processNodes,finallyNodes加入源節點,processNodes加入其他節點 * @param nodeIndex */ private void initDijkstra(int nodeIndex) { finallyNodes = new ArrayList<>(nodes.length); processNodes = new MinHeap(); int[][] node = new int[1][2]; node[0][0] = nodeIndex; node[0][1] = adjacencyMatrix[nodeIndex][nodeIndex]; finallyNodes.add(node); visited[nodeIndex] = true; int[][] process = new int[nodes.length - 1][2]; int j = 0; for (int i = 0 ; i < nodes.length ; i++) { if (i == nodeIndex) { continue; } process[j][0] = i; process[j][1] = adjacencyMatrix[nodeIndex][i]; ++ j; } //初始化最小堆 processNodes.init(process); }
dijsktra演演算法實現
public void dijkstra() { //1。堆頂取出最小元素,加入finallyNodes //2。將與堆頂元素相連節點距離更新, while (!processNodes.isEmpty()) { int[][] head = processNodes.pop(); finallyNodes.add(head); int nodeIndex = head[0][0]; visited[nodeIndex] = true; //跟堆頂元素相鄰的元素 for (int j = 0 ; j < nodes.length ; j ++) { //找到相鄰節點 if (visited[j] || Integer.MAX_VALUE == adjacencyMatrix[nodeIndex][j]) { continue; } for (int i = 0 ; i < processNodes.heap.size() ; i++) { int[][] node = processNodes.heap.get(i); //找到節點並且值變小,需要調整 if (node[0][0] == j && node[0][1] > head[0][1] + adjacencyMatrix[nodeIndex][j]) { processNodes.changeValue(i, head[0][1] + adjacencyMatrix[nodeIndex][j]); break; } } } } }
public static void main(String[] args) { char[] values = new char[]{'A','B','C','D','E','F','G','H'}; String[] edges = new String[]{"0,1,2","0,2,3","0,3,4","1,4,6","2,4,3","3,4,1","4,5,1","4,6,4","5,7,2","6,7,2"}; Dijkstra dijkstra = new Dijkstra(); dijkstra.initGraph(values, edges); int startNodeIndex = 0; dijkstra.initDijkstra(startNodeIndex); dijkstra.dijkstra(); for (int[][] node : dijkstra.finallyNodes) { System.out.println(dijkstra.nodes[node[0][0]].value + "距離" + dijkstra.nodes[startNodeIndex].value + "最短路徑為:" + node[0][1]); } }
以上就是詳解Java中Dijkstra(迪傑斯特拉)演演算法的圖解與實現的詳細內容,更多關於Java Dijkstra演演算法的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45