dijkstra算法
-
对于一个权值非负的有向图,设有两个顶点集S,T;其中S集合中的顶点与源点的的最短已经求出。初始时S集合为空,T集合包含了所有的顶点。
-
设数组D,D[i]表示源点到点i的最短距离,初始时D内源点值为0,其他值为无穷大。
-
从T集合内选出最短距离最小的点,若找不到(T集合为空或者图不连通,有些点源点无法到达),则结束
-
若找到最小的点,则将其加入S集合中,然后用该点的D[i]其更新相邻的D,更新完成后 回到第三步。
package Graph;
public class Dijkstra {
static int MAX = Integer.MAX_VALUE;
static int[][] mp = new int[100][100]; //邻接矩阵
static int[] D = new int[100]; //距离数组
static int[] vis = new int[100]; //vis[i] = 1 表示i在S集合,为0表示i在T集合中
static int n,m; //n个顶点,m跳边。
static void dij(){
for(int i=0;i<100;i++){
D[i] = MAX;
}
D[1] = 0; //源点距离为0
while(true){
int mini = 0,Min = MAX;
for(int j=1;j<=n;j++){
//找出T中最小的D[i]
if(vis[j]==0 && D[j]<Min){
mini = j; Min = D[j];
}
}
if(mini == 0) return;
vis[mini] = 1;
//更新mini连通的顶点
for(int j=1;j<=n;j++){
if(D[j]>D[mini]+mp[mini][j]);
D[j] = D[mini]+mp[mini][j];
}
}
}
}
spfa算法
spfa算法同样可以用来计算单元最短路。它相比于dijkstra算法的有点在于可以计算包含负权边的图,并且可以判断是否存在负环。
算法思想:用一个队列来维护,初始时将源加入到队列中。每次出队一个元素,对所有该点可以到达的点进行松弛操作,若某相邻点松弛成功,则将其入队。队列为空时算法结束。
采用前向星的实现:
package Graph;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
public class Spfa {
static int maxn = 1000001;
class Edge{
int d; //边权值
int to; //连接的下一个顶点
int next; //上一条边
Edge(int d,int to,int next){
this.d = d;
this.to = to;
this.next = next;
}
}
static Edge[] graph = new Edge[maxn]; //存所有的边
static int n; // 顶点数
static int m; // 边数
static int[] head = new int[maxn]; //head[u]表示u起始的最大边的编号
static int num=0; //
static int[] vis = new int[maxn]; // 判断是否在队列中
static int[] D = new int[maxn]; // 距离数组,除源点外,其余点初始化为无穷大
void add(int u,int v,int dis){ //增加一条边
graph[num] = new Edge(dis,v,head[u]);
head[u] = num++;
}
static void spfa(){
Arrays.fill(D, Integer.MAX_VALUE);
Queue<Integer> q = new LinkedList<Integer>();
D[1] = 0;
q.offer(1);
while(!q.isEmpty()){
int u = q.poll();
vis[u] = 0;
// cnt[u]++;
// if(cnt[u] > n){
// //存在负环;
// return;
// }
for(int i=head[u]; i!=-1;i=graph[i].next){
int v = graph[i].to;
if(D[v] > D[u]+graph[i].d){ //松弛
D[v] = D[u]+graph[i].d;
if(vis[v]==0){ //松弛成功,若v没有在队列中,则入队
vis[v] = 1;
q.offer(v);
}
}
}
}
}
}