圖論, 顧名思義就是有圖有論。
圖:由點“Vertex”和邊“Edge ”組成, 且圖分為有向圖和無向圖(本文討論有向圖), 之前做畢業設計的時候研究“多譜流形聚類演算法”的時候有研究“Graph”。 高維資料的聚類就涉及到Graph Cut演算法, 想像資料為歐式空間的點, 資料與資料之間呈現這樣或那樣的聯繫, 資料就是點, 他們的聯繫由邊來決定。 PS:本次學習與聚類演算法無關, 聚類問題具體見之前寫的博客。
論:構建好了目標物件以及目標物件的關係, 接下來的大頭與研究的目的有關。 比如, 圖論最廣泛的應用之一:社交網路。 我與張三是好朋友,
解決最短路徑問題的方法有很多種, 本文主要探討Dijkstra最短路徑演算法, 通過闡述演算法過程, 實現簡單Dijkstra演算法, 來對最短路徑問題有一個比較深刻的瞭解。 其次, 我們知道JAVA作為物件導向的高階語言, 有很多的輪子, 我在穀歌搜了半天發現文檔比較友好且演算法和視覺化都比較良好的開源輪子, 分別是:JGraphT和GraphStream, 這裡只討論JGraphT的調用(因為這個輪子的說明文檔很nice ^$^)。
Dijkstra演算法流程
初始化:稱給定的初始節點為根節點, 給每一個節點設置設定初始化的距離(距離根節點), 其中, 根節點的距離為0(與自身的距離當然是0),這裡, 我並不貼大量代碼演示演算法流程, 只是做一個對比, 討論一下Dijkstra演算法中的演算法複雜度的提升上這麼一個漸進的過程。
下圖圖1來自於維琪百科的引用:
圖1 各最短路徑演算法的提出
上圖1中紅框標注的三種Dijkstra演算法, 第一種方法是使用清單反覆運算的方式來求的最小距離, 部分代碼如下:
for (index = 1; index CostTime[index]) { Vertex = index; MinTime = CostTime[index]; } }以上是C++實現, 上面的代碼中尋找鄰節點最短距離以及對應的節點, 直接遍歷所有未被訪問的節點, 時間複雜度為 O(V^2), 而後面提出的“優先佇列”, 在JAVA中為PriorityQueue, 實際上就是一個堆的實現, 這個佇列存儲當前節點的鄰近點資訊, 並將鄰近點的距離進行排序, 最小的距離排在最前面, 方便取出, 具體實現如下:
//可獲取節點的排序實現 this.availableVertices = new PriorityQueue(vertexKeys.size, new Comparator { @Override public int compare(Vertex one, Vertex two) { int weightOne = Dijkstra.this.distances.get(one.getLabel); int weightTwo = Dijkstra.this.distances.get(two.getLabel); return weightOne - weightTwo; } }); //查找最短距離的節點更新節點的最短距離 while (this.availableVertices.size > 0) { //Pick the closest vertex Vertex next = this.availableVertices.poll; int distanceToNext = this.distances.get(next.getLabel); //便利當前節點的所有鄰節點 List nextNeighbors = next.getNeighbors; for(Edge e: nextNeighbors){ Vertex other = e.getNeighbor(next); if(this.visitedVertices.contains(other)){ continue; } //更新距離 int currentWeight = this.distances.get(other.getLabel); int newWeight = distanceToNext + e.getWeight; if(newWeight以後從這個PriorityQueue中依次存取當前節點的鄰接節點。
第二種和第三者優先佇列, 分別是二叉樹堆(Binary_heap)和斐波那契堆(Fibonacci_heap), 其實, 這些都屬於樹的搜索演算法, 我們常用的一般都是二叉樹, 例如PriorityQueue的實現就是二叉樹最小堆的一個介面, 具體情況可以看一下這個博客對源碼的解析。
接下, 演示一下強大的JGraphT的實現Dijkstra演算法:
package cn.probabilityModel.graphPkg; import java.util.HashMap; import java.util.List; import cn.ODBackModel.utils.NoNameExUtil; import cn.probabilityModel.xmlPkg.XmlParse; import org.jgrapht.alg.interfaces.ShortestPathAlgorithm; import org.jgrapht.alg.shortestpath.DijkstraShortestPath; import org.jgrapht.graph.*; /** * 使用JGraphT求最短距離, 注意JGraphT中的Dijkstra中的最短距離由斐波那契搜索到 * Create the shortest path according to the shortestPath's algorithm * Created by wing1995 on 2017/6/28. */ public class ShortestPath { public static void main(String args[]) { //構建一個加權有向圖 SimpleDirectedWeightedGraph metroGraph = new SimpleDirectedWeightedGraph<>(DefaultWeightedEdge.class); List> stationConnectInfor = XmlParse.getStationConnectInfor; //獲取資料 //創建邊和點以及權重 for(HashMap stationInfo: stationConnectInfor){ String fromStation = NoNameExUtil.NO2Name(stationInfo.get("StatNo")); String toStation = NoNameExUtil.NO2Name(stationInfo.get("NextStatNo")); Double costTime = Double.parseDouble(stationInfo.get("RunningTime2")); metroGraph.addVertex(fromStation); if (toStation != null) { metroGraph.addVertex(toStation); DefaultWeightedEdge metroEdge = metroGraph.addEdge(fromStation, toStation); metroGraph.setEdgeWeight(metroEdge, costTime); } } // 列印從機場東到寶安中心的路徑(都在一條線路) System.out.println("從機場東到寶安中心的最短路徑:"); DijkstraShortestPath dijkstraAlg = new DijkstraShortestPath<>(metroGraph); ShortestPathAlgorithm.SingleSourcePaths iPaths = dijkstraAlg.getPaths("機場東"); System.out.println(iPaths.getPath("寶安中心") + " "); // 列印從機場東到翻身的路徑(不同地鐵線路的兩個網站) System.out.println("從寶安中心到寶華:"); ShortestPathAlgorithm.SingleSourcePaths cPaths = dijkstraAlg.getPaths("寶安中心"); System.out.println(cPaths.getPath("寶華")); } }速度杠杠滴!
結果如下:
注意,上面寶安中心到寶華的最短路徑實際上,寶安中心可以直達寶華,之所以走了寶安中心道新安的方向找最短路徑,是因為,我提供的資料中只創建了一個方向,也就是沒有創建寶安中心沿著寶華方向的有向圖。
結果如下:
注意,上面寶安中心到寶華的最短路徑實際上,寶安中心可以直達寶華,之所以走了寶安中心道新安的方向找最短路徑,是因為,我提供的資料中只創建了一個方向,也就是沒有創建寶安中心沿著寶華方向的有向圖。