1、實驗目的java
經過本實驗的學習理解Dijkstra算法,而且編碼實現最短路徑問題。node
2、實驗內容算法
本Dijkstra算法實驗採用了java實現,因此解決方案也使用了java中的接口,有map映射還有set集合。學習
Dijkstra算法屬於一種貪心算法,它使用了廣度優先搜索來解決帶有權值的有向圖或者無向圖的單源最短路徑問題,該算法不能計算帶有負權值的有向圖或無向圖,該算法的目的是求其餘節點到源的最短路徑。ui
Dijkstra算法採用的是一種貪心的策略,利用Map對象path存儲每一個節點名稱和這個節點到相鄰節點的距離,使用set集合保存已經找到了最短路徑的節點的集合close,用set對象open存儲未處理的節點的集合。初始時,原點 a 的路徑權重被賦爲 0 。同時設全部其餘(a不能直接到達的)節點的路徑長度MAX_VALUE。初始時,close中只有節點s。 而後,從節點a開始,遍歷全部節點,選出距離a節點最短的key值,取代a節點。並把這個最短距離存到path中。而後,咱們須要看看新加入的節點是否能夠到達其餘頂點而且看看經過該節點到達其餘點的路徑長度是否比a點直接到達短,若是是,那麼就替換這些頂點在PATH中的值。 而後,又找下一個裏節點最近的key值,重複上述動做,直到close中包含了圖的全部頂點。 this
3、實驗要求編碼
編碼實現下圖中頂點A到任一頂點的最短路徑:spa
4、實驗過程rest
一、過程分析對象
首先,利用繼承Map接口的HashMap類存儲圖中的信息,其中的key爲圖中的各節點的名稱,value爲各節點與相應節點之間連線的權值。而Map對象Path存儲的是a節點到各個子節點的權值,以及該節點的名稱,另外一個Map對象child中存儲的是各個節點到相鄰節點的權值和該節點的名稱,而Map對象PathInfo存儲的是路徑信息。
以後就是開始實現Dijkstra算法了,本代碼中封裝了一個Dijkstra類,包括getshortestPath,computPath,PrintPathInfo.三個方法。
顧名思義,getshortestPath方法是用來求最短路徑的,他返回的是一個節點,給這個類一個實參,即節點,遍歷圖中依然留在open中的節點,利用以前在child中儲存的權值信息,能夠獲得離實參這個節點最近的節點。其中的關鍵過程就是比較如今的distance和minDis,若是distance小,就會被賦值給minDis,隨着遍歷的進行,minDis不斷被賦值,遍歷結束後,minDis的值就是最短距離,而相對應的key值,就是離實參這個節點最近的子節點。
computPath方法是用來計算節點之間的距離的,該方法的關鍵過程就是首先利用上面的getshortestPath方法獲取離上一個源點最近的子節點nearst,而後在該節點的基礎上,求open中的節點到a點的路徑newCompute,若是newCompute這個值小於Path中儲存的該open中的節點到a節點的距離,那麼就用這個newCompute替換Path中的值,同時更新PathInfo的值。其中newCompute的求法是拿Path中nearst節點對應的權值加上child中該open中的節點到nearst的權值。該類經過遞歸實現Path中的value不斷被更小的值賦值,從而獲得最終的最短路徑。
PrintPathInfo方法是用來打印路徑信息的,打印的是PathInfo中的鍵值。
二、實驗結果
5、實驗總結
關於Dijkstra算法,剛開始作的時候遇到了較大的阻力,爲了更好的理解這個算法的思想,咱們從網上找了四五個關於這個算法的例子,不斷地模擬解決過程,最終獲得了Dijkstra算法的精髓,明白了關於其中的過程。這個過程花了很長時間,以後就是用代碼實現了,由於該算法涉及鍵值對,剛好java語言中有Map接口,因此咱們就選擇了java做爲基礎語言去實現這個Dijkstra算法,其中也是歷經了千辛萬苦,所幸的是,java中關於set集合,和Map接口的子類以及他們的方法讓咱們更加熟悉,經過此次實驗咱們不只進一步的學習了java,也掌握了一門算法,總之,過程是苦的,可是結果很快樂。
感謝教授本實驗的老師,讓咱們獲取了這樣一個吸取知識的機會,只是咱們自身有不少只是漏洞,須要進一步的學習。
附錄(源代碼)
package cn.sal.lisan;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import cn.sal.Lisan2.MapBuilder;
import cn.sal.Lisan2.Node;
class Node {
private String name;
private Map<Node,Integer> child=new HashMap<>();
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;
}
}
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類裏的getchild()方法,再利用散列映射的put方法,將其餘節點和權值關聯後添加進去
nodeA.getChild().put(nodeB, 6);
nodeA.getChild().put(nodeC, 3);
nodeB.getChild().put(nodeA, 6);
nodeB.getChild().put(nodeC, 2);
nodeB.getChild().put(nodeD, 5);
nodeC.getChild().put(nodeA, 3);
nodeC.getChild().put(nodeB, 2);
nodeC.getChild().put(nodeD, 3);
nodeC.getChild().put(nodeE, 4);
nodeD.getChild().put(nodeB, 5);
nodeD.getChild().put(nodeC, 3);
nodeD.getChild().put(nodeF, 3);
nodeD.getChild().put(nodeE, 2);
nodeE.getChild().put(nodeC, 4);
nodeE.getChild().put(nodeD, 2);
nodeE.getChild().put(nodeF, 5);
nodeF.getChild().put(nodeD, 3);
nodeF.getChild().put(nodeE, 5);
open.add(nodeB);
open.add(nodeC);
open.add(nodeD);
open.add(nodeE);
open.add(nodeF);
close.add(nodeA);
return nodeA;
}
}
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_VALUE
path.put("B", 6);
pathInfo.put("B", "A->B");
path.put("C", 3);
pathInfo.put("C", "A->C");
path.put("D", Integer.MAX_VALUE);
pathInfo.put("D", "A->D");
path.put("E", Integer.MAX_VALUE);
pathInfo.put("E", "A");
path.put("F", Integer.MAX_VALUE);
pathInfo.put("F", "A->F");
//將初始節點放入close,其餘節點放入open
Node start = new MapBuilder().build(open, close);
return start;
}
/**
* 獲取與node最近的子節點
*/
private Node getShortestPath(Node node) {
Node res = null;
int minDis = Integer.MAX_VALUE;
Map<Node, Integer> childs = node.getChild();
// 遍歷與Node相鏈接的全部節點,選取其中距離最短的節點
for (Node child : childs.keySet()) {//keyset()方法用來返回鍵即節點
if (open.contains(child)) {
int distance = childs.get(child);
if (distance < minDis) {
minDis = distance;
res = child;
}
}
}
return res;
}
/*
* 計算路徑
*
*
*
*/
public void computePath(Node start) {
//取距離start節點最近的子節點,放入close
Node nearest = getShortestPath(start);
if (nearest == null) {
return;
}
close.add(nearest); //已遍歷的
open.remove(nearest); //未遍歷的
Map<Node, Integer> childs = nearest.getChild();//getChild()返回child 對象,
//child中存儲了各節點到相鄰節點的距離·
for (Node child : childs.keySet()) {//遍歷最近的節點
if (open.contains(child)) {//若是子節點在open中
Integer newCompute = path.get(nearest.getName()) + childs.get(child);
//Map接口的get()方法用來返回key所對應的value,
//此句是用來計算neareset這個節點到每一個子節點的距離
if (newCompute < path.get(child.getName())) {
//新計算出來的距離小於以前設置的距離
path.put(child.getName(), newCompute);
pathInfo.put(child.getName(), pathInfo.get(nearest.getName()) + "->" + child.getName());
}
//本if語句能夠用來更新A到子節點的最短距離
}
}
computePath(start);//重複執行本身,確保全部子節點被遍歷
computePath(nearest);//向外一層層遞歸,直至全部頂點被遍歷
}
public void printPathInfo() {
//entrySet()返回此映射中包含的映射關係的set視圖
Set<Map.Entry<String, String>> pathInfos = pathInfo.entrySet();
for (Map.Entry<String, String> pathInfo : pathInfos) {
System.out.println(pathInfo.getKey() + ":" + pathInfo.getValue());
}
}
}
public class DijkstraTest {
public static void main(String args[]) throws Exception {
Dijkstra test=new Dijkstra();
Node start=test.init();
test.computePath(start);
test.printPathInfo();
}
}