從A到B,有多條路線,要找出最短路線,應該用哪種數據結構來存儲這些數據。
這不是顯然的考查圖論的相關知識了么,
1.圖的兩種表示方式:
鄰接矩陣:二維數組搞定。
鄰接表:Map<Vertext,List<Edge>>搞定。
其中鄰接矩陣適用於稠密圖,即圖上的任意兩點之間均(差不多都)存在一條邊。
而A到B之間的路線,顯然是稀疏圖,果斷的選用鄰接表。
2.加權有向圖最短路徑問題,典型的dijkstra最短路徑算法。
說干就干,翻翻《數據結構與算法》,自己用Java大概實現了一下,具體代碼如下:
實現思路:
1,定義一個類:有向圖類:Graph。
有向圖類的子類:節點類:Vertex,邊類:Vertex。
節點類:保存節點名稱,上一個節點,長度等屬性。
邊節點:保存每條邊的兩邊的節點,通過邊找到對應的另一條節點。
2,該類有兩個屬性:
1,List<Vertex> vertexList:保存圖的頂點集合,便於遍歷頂點的時候查找對應集合。 2,Map<Vertex, List<Edge>> ver_edgeList_map:圖的每個頂點對應的有向邊。
3,為了能夠記錄最短路徑,需要為每個節點定義一個屬性:父節點,表示父節點到該點的距離最短。
3,每個節點有多個屬性:
String name; //節點名字
boolean known; //此節點之前是否已知,如果未知的話,則需要初始化距離adjuDist和parent屬性
int adjuDist; //保存從開始節點到此節點距離
Vertex parent; //當前從初始節點到此節點的最短路徑下,的父節點。
4,從起點節點開始查找。
比較規則:從A節點開始比較,對其指向的B節點進行初始化和比較:
如果B節點未被初始化,先設置該B節點的父節點為A節點,距離為邊長加上A節點的adjuDist。
如果已經初始化完了,則重新比較:
如果A節點加邊長小於B節點的adjuDist,則證明A節點到B節點的距離最短,設置A節點為B節點父節點,並且長度修改為A節點的adjuDist加上邊長。
否則不做操作。
5,等所有的節點初始化完了,從終止節點開始,通過終止節點的父節點找到上一個節點,輸出節點的路徑。
代碼如下:
package 筆試題; import java.util.LinkedList; import java.util.List; import java.util.Map; public class Graph{ private List<Vertex> vertexList; //圖的頂點集 private Map<Vertex, List<Edge>> ver_edgeList_map; //圖的每個頂點對應的有向邊 public Graph(List<Vertex> vertexList, Map<Vertex, List<Edge>> ver_edgeList_map) { super(); this.vertexList = vertexList; this.ver_edgeList_map = ver_edgeList_map; } public List<Vertex> getVertexList() { return vertexList; } public void setVertexList(List<Vertex> vertexList) { this.vertexList = vertexList; } public Map<Vertex, List<Edge>> getVer_edgeList_map() { return ver_edgeList_map; } public void setVer_edgeList_map(Map<Vertex, List<Edge>> ver_edgeList_map) { this.ver_edgeList_map = ver_edgeList_map; } static class Edge{ private Vertex startVertex; //此有向邊的起始點 private Vertex endVertex; //此有向邊的終點 private int weight; //此有向邊的權值 public Edge(Vertex startVertex, Vertex endVertex, int weight) { super(); this.startVertex = startVertex; this.endVertex = endVertex; this.weight = weight; } public Edge() {} public Vertex getStartVertex() { return startVertex; } public void setStartVertex(Vertex startVertex) { this.startVertex = startVertex; } public Vertex getEndVertex() { return endVertex; } public void setEndVertex(Vertex endVertex) { this.endVertex = endVertex; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } } static class Vertex { private final static int infinite_dis = Integer.MAX_VALUE; private String name; //節點名字 private boolean known; //此節點之前是否已知 private int adjuDist; //此節點距離 private Vertex parent; //當前從初始節點到此節點的最短路徑下,的父節點。 public Vertex() { this.known = false; this.adjuDist = infinite_dis; this.parent = null; } public Vertex(String name) { this.known = false; this.adjuDist = infinite_dis; this.parent = null; this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isKnown() { return known; } public void setKnown(boolean known) { this.known = known; } public int getAdjuDist() { return adjuDist; } public void setAdjuDist(int adjuDist) { this.adjuDist = adjuDist; } public Vertex getParent() { return parent; } public void setParent(Vertex parent) { this.parent = parent; } /** * 重新Object父類的equals方法 */ @Override public boolean equals(Object obj) { if (!(obj instanceof Vertex)) { throw new ClassCastException("an object to compare with a Vertext must be Vertex"); } if (this.name==null) { throw new NullPointerException("name of Vertex to be compared cannot be null"); } return this.name.equals(obj); } } public void setRoot(Vertex v) { v.setParent(null); v.setAdjuDist(0); } /** * * @param startIndex dijkstra遍歷的起點節點下標 * @param destIndex dijkstra遍歷的終點節點下標 */ public void dijkstraTravasal(int startIndex,int destIndex) { Vertex start = vertexList.get(startIndex); Vertex dest = vertexList.get(destIndex); String path = "["+dest.getName()+"]"; setRoot(start); updateChildren(vertexList.get(startIndex)); int shortest_length = dest.getAdjuDist(); while((dest.getParent()!=null)&&(!dest.equals(start))) { path = "["+dest.getParent().getName()+"] --> "+path; dest = dest.getParent(); } System.out.println("["+vertexList.get(startIndex).getName() +"] to ["+ vertexList.get(destIndex).getName()+"] dijkstra shortest path :: "+path); System.out.println("shortest length::"+shortest_length); } /** * 從初始節點開始遞歸更新鄰接表 * @param v */ private void updateChildren(Vertex v) { if (v==null) { return; } if (ver_edgeList_map.get(v)==null||ver_edgeList_map.get(v).size()==0) { return; } //用來保存每個可達的節點 List<Vertex> childrenList = new LinkedList<Graph.Vertex>(); for(Edge e:ver_edgeList_map.get(v)) { Vertex childVertex = e.getEndVertex(); //如果子節點之前未知,則進行初始化, //把當前邊的開始點默認為子節點的父節點,長度默認為邊長加邊的起始節點的長度,並修改該點為已經添加過,表示不用初始化 if(!childVertex.isKnown()) { childVertex.setKnown(true); childVertex.setAdjuDist(v.getAdjuDist()+e.getWeight()); childVertex.setParent(v); childrenList.add(childVertex); } //此時該子節點的父節點和之前到該節點的最小長度已經知道了,則比較該邊起始節點到該點的距離是否小於子節點的長度, //只有小於的情況下,才更新該點為該子節點父節點,並且更新長度。 int nowDist = v.getAdjuDist()+e.getWeight(); if(nowDist>=childVertex.getAdjuDist()) { continue; } else { childVertex.setAdjuDist(nowDist); childVertex.setParent(v); } } //更新每一個子節點 for(Vertex vc:childrenList) { updateChildren(vc); } } }
測試代碼:
package 筆試題; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import 筆試題.Graph.Edge; import 筆試題.Graph.Vertex; /** * 測試用main方法 * @author wuhui.wwh * */ public class TestGraph { public static void main(String[] args) { Vertex v1= new Vertex("v1"); Vertex v2= new Vertex("v2"); Vertex v3= new Vertex("v3"); Vertex v4= new Vertex("v4"); Vertex v5= new Vertex("v5"); Vertex v6= new Vertex("v6"); Vertex v7= new Vertex("v7"); Vertex v8= new Vertex("v8"); List<Vertex> verList = new LinkedList<Graph.Vertex>(); verList.add(v1); verList.add(v2); verList.add(v3); verList.add(v4); verList.add(v5); verList.add(v6); verList.add(v7); verList.add(v8); Map<Vertex, List<Edge>> vertex_edgeList_map = new HashMap<Graph.Vertex, List<Edge>>(); List<Edge> v1List = new LinkedList<Graph.Edge>(); v1List.add(new Edge(v1,v2,6)); v1List.add(new Edge(v1,v4,1)); v1List.add(new Edge(v1,v4,1)); List<Edge> v2List = new LinkedList<Graph.Edge>(); v2List.add(new Edge(v2,v3,43)); v2List.add(new Edge(v2,v4,11)); v2List.add(new Edge(v2,v5,6)); List<Edge> v3List = new LinkedList<Graph.Edge>(); v3List.add(new Edge(v3,v8,8)); List<Edge> v4List = new LinkedList<Graph.Edge>(); v4List.add(new Edge(v4,v3,15)); v4List.add(new Edge(v4,v5,12)); List<Edge> v5List = new LinkedList<Graph.Edge>(); v5List.add(new Edge(v5,v3,38)); v5List.add(new Edge(v5,v8,13)); v5List.add(new Edge(v5,v7,24)); List<Edge> v6List = new LinkedList<Graph.Edge>(); v6List.add(new Edge(v6,v5,1)); v6List.add(new Edge(v6,v7,12)); List<Edge> v7List = new LinkedList<Graph.Edge>(); v7List.add(new Edge(v7,v8,20)); vertex_edgeList_map.put(v1, v1List); vertex_edgeList_map.put(v2, v2List); vertex_edgeList_map.put(v3, v3List); vertex_edgeList_map.put(v4, v4List); vertex_edgeList_map.put(v5, v5List); vertex_edgeList_map.put(v6, v6List); vertex_edgeList_map.put(v7, v7List); Graph g = new Graph(verList, vertex_edgeList_map); g.dijkstraTravasal(0, 7); } }
運行結果:
[v1] to [v8] dijkstra shortest path :: [v1] --> [v2] --> [v5] --> [v8]
shortest length::25