1. 程式人生 > >Java鄰接表表示加權有向圖,附dijkstra最短路徑演算法

Java鄰接表表示加權有向圖,附dijkstra最短路徑演算法

圖這種adt(abstract data type)及相關的演算法,之前一直是我未曾涉足過的領域。

主要是作為一個小測試,在平常的工作中也用不著,就算面試,至今也未曾碰到過相關考題。

但是前幾天,原公司的小美女談到面試過程中就碰到一題:

從A到B,有多條路線,要找出最短路線,應該用哪種資料結構來儲存這些資料。

等等,這不是顯然的考查圖論的相關知識了麼,

1.圖的兩種表示方式:

鄰接矩陣:二維陣列搞定。

鄰接表:Map<Vertext,List<Edge>>搞定

其中臨街矩陣適用於稠密圖,即圖上的任意兩點之間均(差不多都)存在一條邊。

而A到B之間的路線,顯然是稀疏圖,果斷的選用鄰接表。

2.加權有向圖最短路徑問題,典型的dijkstra最短路徑演算法。

測試用的圖示例::

說幹就幹,翻翻《資料結構與演算法》,自己用Java大概實現了一下,可能有bug,歡迎交流,具體程式碼如下:

package base.algorithm;

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);
			}
			
			//子節點之前已知,則比較子節點的ajduDist&&nowDist
			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 base.algorithm;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import base.algorithm.Graph.Edge;
import base.algorithm.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(1, 5);
		g.dijkstraTravasal(0, 7);
	}
}

測試結果: