圖算法系列之最短路算法Dijkstra(Java)

引言java

假如你有一張地圖,地圖上給出了每一對相鄰城市的距離,從一個地點到另一個地點,如何找到一條最短的路? 最短路算法要解決的就是這類問題。今年的華爲精英挑戰賽codeCraft中關於部署大數據下網絡服務器部署問題就須要使用最短路算法,由於求最小流最大費用算法時, 最核心的就是找出最短路,可見最短路算法的應用之普遍。算法

一.定義:數組

給定一個有(無)向圖,每一條邊有一個權值 w,給定一個起始點 S 和終止點 T ,求從 S 出發走到 T 的權值最小路徑,即爲最短路徑。最短路算法依賴一種性質:一條兩頂點間的最短路徑包含路徑上其餘最短路徑。簡單的說就是:最短路徑的子路徑是最短路徑。服務器

二.最短路算法網絡

最經常使用的最短路算法是Dijkstra算法、A*算法、SPFA算法、Bellman-Ford算法和Floyd-Warshall算法,咱們這裏重點介紹並實現Dijkstra和SPFA,以及A*算法,其餘算法只介紹原理,不展開app

1.鬆弛技術(Relaxation)(很是重要)大數據

鬆弛操做的原理是著名的定理:「三角形兩邊之和大於第三邊」,在信息學中咱們叫它三角不等式。所謂對結點i,j進行鬆弛,就是斷定是否dis[j]>dis[i]+w[i,j],若是該式成立則將dis[j]減少到dis[i]+w[i,j],不然不動。ui

2.Dijkstra算法this

解決最短路問題,最經典的算法是 Dijkstra算法,它是一種單源最短路算法,其核心思想是貪心算法(Greedy Algorithm),Dijkstra算法由荷蘭計算機科學家Dijkstra發現,這個算法至今差很少已有50年曆史,可是由於它的穩定性和通俗性,到如今依然強健。另外,Dijkstra算法要求全部邊的權值非負。spa

1) Dijkstra算法思想爲:

設 G = (V, E) 是一個帶權有向圖,把圖中頂點集合 V 分紅兩組,第一組爲已求出最短路徑的頂點集合(用 S 表示,初始時 S 中只有一個源點,之後每求得一條最短路徑 , 就將其加入到集合 S 中,直到所有頂點都加入到 S 中,算法就結束了),第二組爲其他未肯定最短路徑的頂點集合(用 U 表示),按最短路徑長度的遞增次序依次把第二組的頂點加入 S 中。在加入的過程當中,總保持從源點 v 到 S 中各頂點的最短路徑長度不大於從源點 v 到 U 中任何頂點的最短路徑長度。此外,每一個頂點對應一個距離,S 中的頂點的距離就是從 v 到此頂點的最短路徑長度,U 中的頂點的距離,是從 v 到此頂點只包括 S 中的頂點爲中間頂點的當前最短路徑長度。

2)算法核心步驟以下:

    a. 將全部頂點分爲兩部分:已知最短路程的頂點集合P和未知最短路徑的頂點集合Q。最開始,已知最短路徑的頂點集合P中只有源點一個頂點。咱們這裏用一個isVisited數組來記錄哪些頂點再集合P中。例如對於某個頂點i,若是isVisited[i]爲1則表示這個頂點再集合P中,若是isVisited[i]爲0則表示這個頂點再集合Q中。
    b. 設置源點s到本身的最短路徑爲0即 dis[s]=0。若存在有源點能直接到達的頂點i,則把dis[i]設爲w[s][i]。同時把全部其餘(源點不能直接到達的)頂點的最短路徑設爲∞。
    c. 在集合Q的全部頂點中選擇一個離源點s最近的頂點u(即dis[u]最小)加入到集合P。並考察全部以點u爲起點的邊,對每一條邊進行鬆弛操做。例如存在一條從u到v的邊,那麼能夠經過將u->v添加到尾部來拓展一條從s到v的路徑,這條路徑的長度時dis[u]+w[u][v]。若是這個值比目前已知的dis[v]的值要小,咱們能夠用新值來替代當前dis[v]中的值。

    d. 重複第3步,若是集合Q爲空,算法結束。最終dis數組中的值就是源點到全部頂點的最短路徑。

補充:dis數組用來記錄起點到全部頂點的距離,Path[]數組,Path[i]表示從S到i的最短路徑中,結點i以前的結點的編號。注意,是「以前」,不是「以後」。最短路徑算法的核心思想成爲「鬆弛」,原理是三角形不等式,咱們只須要在藉助結點u對結點v進行鬆弛的同時,標記下Path[v] = u,記錄的工做就完成了。

3)算法案例圖解

 計算從源頂點1到其它頂點間的最短路徑,對下圖中的有向圖,應用Dijkstra算法計算從源頂點1到其它頂點間最短路徑的過程列在下面的表中。

http://www.layz.net/LAOJ/suanfa/pic/s9-3-11.gif

Dijkstra算法的迭代過程:

http://www.layz.net/LAOJ/suanfa/pic/s9-3-12.gif

http://www.layz.net/LAOJ/suanfa/pic/s9-3-13.gif

http://www.layz.net/LAOJ/suanfa/pic/s9-3-15.gif

三.最短路算法Java實現

1.頂點結構

public class Vertex {  
    //存放點信息  
    public int data;  
    //與該點鄰接的第一個邊節點  
    public Edge firstEdge;  
    }

2.邊結構

//邊節點  
    public class Edge {  
    //對應的點下表  
    public int vertexId;  
    //邊的權重  
    public int weight;  
    //下一個邊節點  
    public Edge next;  
    //getter and setter自行補充  
    }

3.圖結構

import java.util.*;  
public class graph {  
public Vertex[] vertexList; //存放點的集合  
public graph(int vertexNum){  
    this.vertexNum=vertexNum;  
    vertexList=new Vertex[vertexNum];  
}  
//點個數  
public int vertexNum;  
//邊個數  
public int edgeLength;  
public void initVertext(){  
    for(int i=0;i<vertexNum;i++){  
        Vertex vertext=new Vertex();  
        vertext.firstEdge=null;  
        vertexList[i]=vertext;  
        //System.out.println("i"+vertexList[i]);  
    }  
}  
//針對x節點添加邊節點y  
public void addEdge(int x,int y,int weight){  
    Edge edge=new Edge();  
    edge.setVertexId(y);  
    edge.setWeight(weight);  
    //第一個邊節點  
    if(null==vertexList[x].firstEdge){  
        vertexList[x].firstEdge=edge;  
        edge.setNext(null);  
    }  
    //不是第一個邊節點,則採用頭插法  
    else{  
        edge.next=vertexList[x].firstEdge;  
        vertexList[x].firstEdge=edge;  
    }  
}

4.Dijkstra算法

package MSP;
import java.util.*;
public class Dijkstra {
public graph gh;
public Dijkstra(graph gh){
	this.gh=gh;
}
//未求出最短路徑的點集合
public ArrayList<Integer> unVisited=new ArrayList();
//已求出最短路徑的點集合
public ArrayList hasVisited=new ArrayList();
//記錄從起點到其餘任意一點的路徑長度
public int distances[];
//記錄Path[i]表示從S到i的最短路徑中,結點i以前的結點的編號,即對應點的前一個節點
public int paths[];
/**
* 初始化各點距離及路徑
*/
public void init(int x,int y ){
distances=new int[gh.vertexNum];
paths=new int[gh.vertexNum];
for(int i=0;i<distances.length;i++){
	distances[i]=Integer.MAX_VALUE;
}
distances[x]=0;
//把與x相鄰的點的距離求出,並標準初始路徑
for(Edge edge=gh.vertexList[x].firstEdge;edge!=null;edge=edge.next){
	distances[edge.vertexId]=edge.weight;
	paths[edge.vertexId]=x;
}
//初始化未知最短路點集合和已知最短路集合
unVisited.clear();
hasVisited.clear();
hasVisited.add(x);
//其他點爲未知
for(int i=0;i<gh.vertexList.length;i++){
	if(i!=x){
		unVisited.add(i);
	}
}
}
/**
* 求從x到y的最短路徑,並返回該路徑
* @param x
* @param y
*/
public void Dijkstra(int x,int y){
init(x,y);
//若是
System.out.println("開始執行...");
while(!unVisited.isEmpty()){
	int vertexId=pickMinInUnvisited(x);
	if(vertexId==-1)
		break;
	//將其加入到已hasvisited集合中,並從未訪問列表中去除
	hasVisited.add(vertexId);
	unVisited.remove((Integer)vertexId);
	//對u爲起點,相鄰的點爲終點的臨接點進行鬆弛操做
	relax(vertexId);
}
for(int i=0;i<distances.length;i++){
	System.out.println(x+"-->"+i+"距離爲"+distances[i]);
}
ArrayList mypath=printPath(x,y);
StringBuilder sb=new StringBuilder();
sb.append("路徑爲:");
for(int i=0;i<mypath.size();i++){
	sb.append(mypath.get(i)+"-->");
}
sb.delete(sb.length()-3,sb.length());
System.out.println(sb.toString());
}
/**
 * 求出從x到y的路徑,因爲path中存放的該點的前一個點的位置
 * @param x
 * @param y
 */
public ArrayList<String> printPath(int x,int y){
	ArrayList mypaths=new ArrayList();
	while(y!=x){
		mypaths.add(y);
		y=paths[y];
	}
	mypaths.add(x);
	//路徑倒置,須要反置回來
	Collections.reverse(mypaths);
	return mypaths;
}
/**
* 考察全部以點u爲起點的邊,對每一條邊進行鬆弛操做。
* @param u
*/
public void relax(int u){
for(Edge edge=gh.vertexList[u].firstEdge;edge!=null;edge=edge.next){
	int v=edge.vertexId;
	//對v進行鬆弛,看是否知足distances[v]>distances[u]+w[u][v]
	int w=edge.weight;
	if(distances[v]>distances[u]+w){
		distances[v]=distances[u]+w;
		//記錄v的最短路徑時,前一個節點爲u
		paths[v]=u;
	}
}
}
/**
* 從未求出最短路徑的集合中找到與原點最近的點
* @param x
*/
public int pickMinInUnvisited(int x){
int minIndex=-1;
int min=Integer.MAX_VALUE;
for(int i=0;i<distances.length;i++){
	if(unVisited.contains(i)){
		if(distances[i]<min){
			minIndex=i;
			min=distances[i];
		}
	}
}
return minIndex;
}
public static void main(String[]args){  
graph g=new graph(5);  
g.initVertext();  
g.addEdge(0,1,2);  
g.addEdge(0,2,2);  
g.addEdge(1,4,1);  
g.addEdge(1,3,3);  
g.addEdge(2,3,3);  
g.addEdge(4,3,1);
Dijkstra dj=new Dijkstra(g);
dj.Dijkstra(1,3);
}  
}

執行結果以下:

1-->0距離爲2147483647
1-->1距離爲0
1-->2距離爲2147483647
1-->3距離爲2
1-->4距離爲1
路徑爲:1-->4-->3

 

親,若是您感受本文有用,請點個贊再走吧✌(>‿◠)!!

相關文章
相關標籤/搜索