最短路径的几种算法。
1. dijkstra 朴素
2. dijkstra堆优化版本
3 .bellman-ford算法
4. spfa
5. 弗洛伊德多源
1.朴素dijkstra算法
//迪杰斯特拉
boolean[] st = new boolean[n+1]; //标记是否已经选入集合
int[][] g = new int[n+1][n+1]; //邻接矩阵
int[] dist = new int[n+1]; //单源距离
Arrays.fill(dist,Integer.MAX_VALUE/2);
dist[k]=0;
//初始化图
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++){
if (i==j) g[i][j]=0;
else g[i][j]=Integer.MAX_VALUE/2;
}
for (int[] time:times){
g[time[0]][time[1]] = time[2];
}
//进行n次选点
for (int i=1;i<=n;i++){
//选出距离源点最近的未选入集合的点
int minV = -1;
for (int i1=1;i1<=n;i1++){
if (!st[i1] && (minV==-1 || dist[minV]>dist[i1])){
minV=i1;
}
}
st[minV] = true; //该点加入选中集合
//用该点更新到其他点的距离
for (int i1=1;i1<=n;i1++){
if (g[minV][i1] <Integer.MAX_VALUE/2 && g[minV][i1]+dist[minV] < dist[i1] )
dist[i1]=g[minV][i1]+dist[minV];
}
}
int leastMax = -1;
for (int i=1;i<=n;i++){
leastMax=Math.max(leastMax,dist[i]);
}
return leastMax>=Integer.MAX_VALUE/2 ? -1:leastMax;
2.堆优化版dijkstra
//堆优化版本迪杰斯特拉 ----》选最小节点时用堆选取
boolean[] st = new boolean[n+1]; //标记是否已经选入集合
int[][] g = new int[n+1][n+1]; //邻接矩阵
int[] dist = new int[n+1]; //单源距离
Arrays.fill(dist,Integer.MAX_VALUE/2);
dist[k]=0;
//初始化图
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++){
if (i==j) g[i][j]=0;
else g[i][j]=Integer.MAX_VALUE/2;
}
for (int[] time:times){
g[time[0]][time[1]] = time[2];
}
PriorityQueue<int[]> pq = new PriorityQueue<>((a,b)-> a[0]-b[0]);
pq.add(new int[]{0,k});
//进行n次选点
while(!pq.isEmpty()) {
//选出距离源点最近的未选入集合的点 --------这里采用堆
int minV = pq.poll()[1];
if (st[minV]) continue;
st[minV] = true; //该点加入选中集合
//用该点更新到其他点的距离
for (int i1=1;i1<=n;i1++){
if (g[minV][i1] <Integer.MAX_VALUE/2 && g[minV][i1]+dist[minV] < dist[i1] ){
dist[i1]=g[minV][i1]+dist[minV];
pq.add(new int[]{dist[i1],i1});
}
}
}
int leastMax = -1;
for (int i=1;i<=n;i++){
leastMax=Math.max(leastMax,dist[i]);
}
return leastMax>=Integer.MAX_VALUE/2 ? -1:leastMax;
3. bellman-ford
// ======================bellman-ford=========================
//进行K次循环-》代表最多走k条边 ,然后每次更新最小距离,需要用到一个backup数组
int[] dist = new int[n+1]; //单源距离
int[] backup = new int[n+1];
Arrays.fill(dist,Integer.MAX_VALUE/2);
dist[k]=0;
//不需要初始化图,直接对所有边遍历
for(int i=0;i<times.length;i++){
backup = Arrays.copyOf(dist,n+1);
for(int[] time:times){
int a = time[0],b=time[1],w=time[2];
if (dist[b]>backup[a]+w) dist[b]=backup[a]+w;
}
}
int leastMax = -1;
for (int i=1;i<=n;i++){
leastMax=Math.max(leastMax,dist[i]);
}
return leastMax>=Integer.MAX_VALUE/2 ? -1:leastMax;
4.spfa
class Solution {
public int networkDelayTime(int[][] times, int n, int k) {
//=================== SPFA ==================
//使用队列存储只更新了最小距离的点。只有这些点更小了别的点的距离才可能更小
int[] dist = new int[n+1]; //单源距离
boolean[] st = new boolean[n+1]; //标记是否在队列中
int[][] g = new int[n+1][n+1]; //邻接矩阵
Arrays.fill(dist,Integer.MAX_VALUE/2);
dist[k]=0;
Queue<Integer> queue = new ArrayDeque<>();
queue.add(k);
st[k] = true;
//初始化图
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++){
if (i==j) g[i][j]=0;
else g[i][j]=Integer.MAX_VALUE/2;
}
for (int[] time:times){
g[time[0]][time[1]] = time[2];
}
while (!queue.isEmpty()){
int ver = queue.poll();
st[ver] = false;
for (int i=1;i<=n;i++){
if (g[ver][i]<Integer.MAX_VALUE/2 && dist[i]>dist[ver]+g[ver][i]){
if (!st[i]){
queue.add(i);
st[i]=true;
}
dist[i] = dist[ver]+g[ver][i];
}
}
}
int leastMax = -1;
for (int i=1;i<=n;i++){
leastMax=Math.max(leastMax,dist[i]);
}
return leastMax>=Integer.MAX_VALUE/2 ? -1:leastMax;
}
}
5.多源:弗洛伊德
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Queue;
class Solution {
public int networkDelayTime(int[][] times, int n, int k) {
//弗洛伊德算法
int[][] g = new int[n+1][n+1]; //邻接矩阵
//初始化图
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++){
if (i==j) g[i][j]=0;
else g[i][j]=Integer.MAX_VALUE/2;
}
for (int[] time:times){
g[time[0]][time[1]] = time[2];
}
for (int k1=1;k1<=n;k1++){
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++)
g[i][j]=Math.min(g[i][j],g[i][k1]+g[k1][j]);
}
}
int leastMax = -1;
for (int i=1;i<=n;i++){
leastMax=Math.max(leastMax,g[k][i]);
}
return leastMax>=Integer.MAX_VALUE/2 ? -1:leastMax;
}
}
6. 写一个邻接表用spfa求解的方法:
import java.beans.IntrospectionException;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Queue;
class Solution {
public static int N = 6010; //定义最大的边数
int idx=0;
int[] h ; //对应节点数
int[] e = new int[N]; //边对应的节点
int[] ne = new int[N]; //下一条边的下标
int[] w = new int[N]; //每条边对应的权重
//添加一条a指向b权重为c的边
public void add(int a, int b, int c){
e[idx]=b; w[idx]=c; ne[idx]=h[a]; h[a]=idx++;
}
public int networkDelayTime(int[][] times, int n, int k) {
h= new int[n+1];
int[] dist = new int[n+1];
Arrays.fill(h,-1);
Arrays.fill(dist,Integer.MAX_VALUE/2);
dist[k]=0;
//建图
for (int[] time:times){
add(time[0],time[1],time[2]);
}
//SPFA
Queue<Integer> queue = new ArrayDeque<>();
boolean[] st = new boolean[n+1];
queue.add(k);
st[k]=true;
while(!queue.isEmpty()){
int ver = queue.poll();
st[ver] = false;
for (int i=h[ver];i!=-1;i=ne[i]){
int verB = e[i]; //这才是过去的点,注意了!
if (dist[verB]>dist[ver]+w[i]){
//只有不在队列内时,才需要加入队列中
if (!st[verB]){
st[verB]=true;
queue.add(verB);
}
dist[verB]=dist[ver]+w[i];
}
}
}
int leastMax = -1;
for (int i=1;i<=n;i++){
leastMax=Math.max(leastMax,dist[i]);
}
return leastMax>=Integer.MAX_VALUE/2 ? -1:leastMax;
}
}