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


從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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM