任務描述:在一個無向圖中,擷取起始節點到所有其他節點的最短路徑描述
Dijkstra(迪傑斯特拉)演算法是典型的最短路徑路由演算法,用於計算一個節點到其他所有節點的最短路徑。主要特點是以起始點為中心向外層層擴充,直到擴充到終點為止。
Dijkstra一般的表述通常有兩種方式,一種用永久和臨時標號方式,一種是用OPEN, CLOSE表方式
用OPEN,CLOSE表的方式,其採用的是貪進法的演算法策略,大概過程如下:
1.聲明兩個集合,open和close,open用於儲存未遍曆的節點,close用來儲存已遍曆的節點
2.初始階段,將初始節點放入close,其他所有節點放入open
3.以初始節點為中心向外一層層遍曆,擷取離指定節點最近的子節點放入close並從新計算路徑,直至close包含所有子節點
代碼執行個體如下:
Node對象用於封裝節點資訊,包括名字和子節點
public class Node {private String name;private Map<Node,Integer> child=new HashMap<Node,Integer>();public Node(String name){this.name=name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Map<Node, Integer> getChild() {return child;}public void setChild(Map<Node, Integer> child) {this.child = child;}}
MapBuilder用於初始化資料來源,返回圖的起始節點
public class MapBuilder {public Node build(Set<Node> open, Set<Node> close){Node nodeA=new Node("A");Node nodeB=new Node("B");Node nodeC=new Node("C");Node nodeD=new Node("D");Node nodeE=new Node("E");Node nodeF=new Node("F");Node nodeG=new Node("G");Node nodeH=new Node("H");nodeA.getChild().put(nodeB, 1);nodeA.getChild().put(nodeC, 1);nodeA.getChild().put(nodeD, 4);nodeA.getChild().put(nodeG, 5);nodeA.getChild().put(nodeF, 2);nodeB.getChild().put(nodeA, 1);nodeB.getChild().put(nodeF, 2);nodeB.getChild().put(nodeH, 4);nodeC.getChild().put(nodeA, 1);nodeC.getChild().put(nodeG, 3);nodeD.getChild().put(nodeA, 4);nodeD.getChild().put(nodeE, 1);nodeE.getChild().put(nodeD, 1);nodeE.getChild().put(nodeF, 1);nodeF.getChild().put(nodeE, 1);nodeF.getChild().put(nodeB, 2);nodeF.getChild().put(nodeA, 2);nodeG.getChild().put(nodeC, 3);nodeG.getChild().put(nodeA, 5);nodeG.getChild().put(nodeH, 1);nodeH.getChild().put(nodeB, 4);nodeH.getChild().put(nodeG, 1);open.add(nodeB);open.add(nodeC);open.add(nodeD);open.add(nodeE);open.add(nodeF);open.add(nodeG);open.add(nodeH);close.add(nodeA);return nodeA;}}圖的結構如下圖所示:
Dijkstra對象用於計算起始節點到所有其他節點的最短路徑
public class Dijkstra {Set<Node> open=new HashSet<Node>();Set<Node> close=new HashSet<Node>();Map<String,Integer> path=new HashMap<String,Integer>();//封裝路徑距離Map<String,String> pathInfo=new HashMap<String,String>();//封裝路徑資訊public Node init(){//初始路徑,因沒有A->E這條路徑,所以path(E)設定為Integer.MAX_VALUEpath.put("B", 1);pathInfo.put("B", "A->B");path.put("C", 1);pathInfo.put("C", "A->C");path.put("D", 4);pathInfo.put("D", "A->D");path.put("E", Integer.MAX_VALUE);pathInfo.put("E", "A");path.put("F", 2);pathInfo.put("F", "A->F");path.put("G", 5);pathInfo.put("G", "A->G");path.put("H", Integer.MAX_VALUE);pathInfo.put("H", "A");//將初始節點放入close,其他節點放入openNode start=new MapBuilder().build(open,close);return start;}public void computePath(Node start){Node nearest=getShortestPath(start);//取距離start節點最近的子節點,放入closeif(nearest==null){return;}close.add(nearest);open.remove(nearest);Map<Node,Integer> childs=nearest.getChild();for(Node child:childs.keySet()){if(open.contains(child)){//如果子節點在open中Integer newCompute=path.get(nearest.getName())+childs.get(child);if(path.get(child.getName())>newCompute){//之前設定的距離大於新計算出來的距離path.put(child.getName(), newCompute);pathInfo.put(child.getName(), pathInfo.get(nearest.getName())+"->"+child.getName());}}}computePath(start);//重複執行自己,確保所有子節點被遍曆computePath(nearest);//向外一層層遞迴,直至所有頂點被遍曆}public void printPathInfo(){Set<Map.Entry<String, String>> pathInfos=pathInfo.entrySet();for(Map.Entry<String, String> pathInfo:pathInfos){System.out.println(pathInfo.getKey()+":"+pathInfo.getValue());}}/** * 擷取與node最近的子節點 */private Node getShortestPath(Node node){Node res=null;int minDis=Integer.MAX_VALUE;Map<Node,Integer> childs=node.getChild();for(Node child:childs.keySet()){if(open.contains(child)){int distance=childs.get(child);if(distance<minDis){minDis=distance;res=child;}}}return res;}}
Main用於測試Dijkstra對象
public class Main {public static void main(String[] args) {Dijkstra test=new Dijkstra();Node start=test.init();test.computePath(start);test.printPathInfo();}}
列印輸出如下:
D:A->D
E:A->F->E
F:A->F
G:A->C->G
B:A->B
C:A->C
H:A->B->H