title: Dijikstra算法实现
date: 2020-04-09 23:04:09
Dijikstra算法是图论里面的经典算法,用来算某一源点到其他各个点的最短路径的问题。
算法的步骤
1.准备工作:
(1).首先设置一个顶点集和S记录已经求得的最短路径的点集合,V初始时所有点的集和,初始时把源点v0从V中放入S,集和S每次加入一个新的点vi,都要更新v0到V-S中各点的最短路径值
(2).设置一个distance数组用于记录源点到当前点的最短路径值(distance[i]表示源点到i号节点的最短路径)
(3).设置一个pronode数组用于存放源点到当前节点时要经过的i的前驱节点(例如:0到3的最短路径是0-1-2-3;则distan[3]=2)用来在算法结束后根据数组找到他的最短路径信息
(4).邻接矩阵weight[i][j]表示从i节点到j节点的权重,如果存在i到j的边则weight[i][j]为边权重,否则其值为∞
2.实现步骤:
(1)初始化:集和S初始为{0},distance[]的初始值distance[i]=weight[0][i],i=1,2…n-1
(2)从顶点集和V-S中选出vj满足vj=min{distance[i]|vi∈(V-S)} (就是从未确定最短路径的点集合中,选出离源点最近的那个点,这里就是贪心的思想),此时vj就是当前求得的一条从v0出发的最短路径的终点,把vj加入到已知最短路径的点集合S中去:S=S U{j}
(3)修改从v0出发到集和(V-S)上任一点vk的最短路径值,如果distance[j]+weight[j][k]<distance[k],那就把distance[k]更新为distance[j]+weight[j][k],(意思是从v0到vk在不经过vj的情况下距离大于v0通过vj到vk的距离时,松弛他),
(4)重复2~3操作共n-1次
举例如下图:
顶点 | 第一轮 | 第二轮 | 第三轮 | 第四轮 |
---|---|---|---|---|
2 | 10 (v1-v2) | 8 (v1-v5-v2) | 8 (v1-v5-v2) √ | |
3 | ∞ | 14 (v1-v5-v3) | 13 (v1-v5-v4-v3) | 9 {v1-v5-v2-v3} √ |
4 | ∞ | 7 (v1-v5-v4) √ | ||
5 | 5(v1-v5) √ | |||
集和S | {1,5} | {1,5,4} | {1,5,4,2} | {1,5,4,2,3} |
执行过程:
初始化(第一轮):集和S为{v1},v1到v2,v5可达,到v3,v4不可达,因此distance数组为dista[2]=10,distance[5]=5;distance[3]=distance[4]=∞;选出最小的distace[5]将5号节点加入S中。
第二轮:根据新加入的节点5,更新distance数组(只更新未被加入S中的点)distance[3]=∞,distance[5]+weight[5][3]<distance[3],故更新distance[3]=distance[5]+weight[5][3] ;同理更新distance[2]=min(distance[2],distance[5]+weight[5][2]),distance[4]=min(distance[4],distance[5]+weight[5][4]);选出最短路径点v4 把4号节点放入S中。
第三轮:根据新加入的节点4,更新distance数组;distance[2]=min(distance[2],distance[4]+weight[4][2]),distance[3]=min(distance[3],distance[3]+weight[4][3]);
第四轮:根据新加入的节点2更新distance数组distance[3]=min(distance[3],distance[2]+weight[2][3])=9.
为什么Dijikstra算法时不允许有负权重的?
如果存在负权重,则S里面的某个点a以负权重相连的b点确定其最短路径时如果存在负权重,则S里面的某个点a与以负权重相连的b点确定其最短路径时,可能存在经过负权重后的路径才是最短路径,但此时a已经在S中无法在继续更新
如下两种情况都会导致Dijikstra算法失效
对于情况1,以1为源点,把节点3加入后,当再加入节点2时,原来的S里已经有节点3,但此时0-2-3才是最短路径,但无法更新0-3的距离。
对于情况2,存在负回路,就不存在最短路径。
实现的代码如下:参考链接: 参考
import java.util.LinkedList;
import java.util.Stack;
class Edge1{
private int v1;
private int v2;
private int weight;
public Edge1(int v1,int v2,int weight){
this.v1=v1;
this.v2=v2;
this.weight=weight;
}
public int getV1(){
return v1;
}
public int getV2(){
return v2;
}
public int getWeight(){
return weight;
}
public boolean equals(Edge1 edge){
return this.v1==edge.getV1()&&this.v2==edge.getV2()&&this.weight==edge.getWeight();
}
public String toString(){
String str="["+v1+","+v2+","+weight+"]";
return str;
}
}
class Graph1{
private LinkedList<Edge1>[] edgeLinks;//顶点的边的集和
private int vNum; //顶点总数
private int edgeNum; //边总数
private int[] distance; //存放远点到i点距离
private int[] prenode;
private LinkedList<Integer> S; //已知的最短路径的点集和
private LinkedList<Integer> Q; //不确定的最短路径的点集和
public static final int INF=10000; //无穷大
public static final int NIL=-1; //不存在
public Graph1(int vNum){
this.vNum=vNum;
edgeLinks=new LinkedList[vNum];
edgeNum=0;
distance=new int[vNum];
prenode=new int[vNum];
for(int i=0;i<vNum;i++)
edgeLinks[i]=new LinkedList<>(); //每一个顶点new一个以他作为其起始点的边集和
}
public void insertEdge(Edge1 edge){
int v1=edge.getV1();
edgeLinks[v1].add(edge); //给以点v1为起始点的边的集和里加入edge这条边
edgeNum++;
}
public void travel(){
System.out.println("共有:"+vNum+"个顶点,"+edgeNum+" 条边");
for (int i=0;i<vNum;i++){
LinkedList<Edge1> list=(LinkedList<Edge1>)edgeLinks[i].clone();
while (!list.isEmpty()){
Edge1 edge=list.pop();
System.out.println(edge.toString());
}
}
}
/**
* 对最短路径估计和前驱节点初始化
*/
public void Init_Single_Source(int start){
for (int i=0;i<vNum;i++){
distance[i]=INF;
prenode[i]=NIL;
}
distance[start]=0;
}
public void relax(Edge1 edge){
int v1=edge.getV1();
int v2=edge.getV2();
int w=edge.getWeight();
if(distance[v2]>distance[v1]+w){
distance[v2]=distance[v1]+w;
prenode[v2]=v1;
}
}
public int ECTRACT_MIN(LinkedList<Integer> q){
if(q.isEmpty())
return -1;
int min=q.getFirst();
for(int i=0;i<q.size();i++){
int v=q.get(i);
if(distance[min]>distance[v])
min=v;
}
q.remove(q.indexOf(min));
return min;
}
public void Dijistra(int start){
Init_Single_Source(start);
S=new LinkedList<>();
Q=new LinkedList<>();
for (int i=0;i<vNum;i++) //将所有开始的点集加入到Q中
Q.add(i);
while (!Q.isEmpty()){
int v=ECTRACT_MIN(Q);
S.add(v);
LinkedList<Edge1> list=(LinkedList<Edge1>)edgeLinks[v].clone();
while (!list.isEmpty()){
Edge1 edge1=list.pop();
relax(edge1);
}
showResult();
}
}
public void showResult(){
System.out.println("-----------------result-------------------");
Stack<Integer>[] routes=new Stack[vNum];
for (int i=0;i<vNum;i++){
routes[i]=new Stack<>();
int j=i;
while (j!=NIL){
routes[i].push(j);
j=prenode[j];
}
System.out.print(i+"("+distance[i]+"): ");
while (!routes[i].isEmpty()){
int k=routes[i].pop();
System.out.print("-->"+k);
}
System.out.println();
}
}
}
public class Dijkstra {
public static void main(String[] args){
int vnum=5,edgeNum=10;
Graph1 graph1=new Graph1(vnum);
Edge1[] edge1s=new Edge1[edgeNum];
edge1s[0]=new Edge1(0,1,10);
edge1s[1]=new Edge1(0,3,5);
edge1s[2]=new Edge1(1,2,1);
edge1s[3]=new Edge1(1,3,2);
edge1s[4]=new Edge1(2,4,4);
edge1s[5]=new Edge1(3,1,3);
edge1s[6]=new Edge1(3,2,9);
edge1s[7]=new Edge1(3,4,2);
edge1s[8]=new Edge1(4,0,7);
edge1s[9]=new Edge1(4,2,6);
for (int i=0;i<edgeNum;i++)
graph1.insertEdge(edge1s[i]);
graph1.travel();
graph1.Dijistra(0);
// graph1.Dijistra(4);
}
}