符号假设
n n n:点的个数
m:边的个数
算法 | 求解问题 | 特点 | 时间复杂度 |
---|---|---|---|
朴素dijkstra算法 | 单源最短路径 | 边权为正(稠密图) | O ( n 2 ) O(n^2) O(n2) |
堆优化的dijkstra算法 | 单源最短路径 | 边权为正(稀疏图) | O ( m l o g n ) O(mlogn) O(mlogn) |
Bellman_Ford | 单源最短路 | 存在负权边,(限k边) | O ( n m ) O(nm) O(nm) |
SPFA(BF算法的优化版本) | 单源最短路 | 存在负权边 | 一般O(m),最坏O(nm) |
Flyod | 多源最短路径 | g进行存储距离,每个赋值INF | O ( n 3 ) O(n^3) O(n3) |
算法流程
距离初始化:d[i]=INF,d[1]=0
for i in (1,…n):
2.1寻找集合S中距离源点最近的点,(S,未确定最短距离的点)
2.2标记该点
2.3用该点更新其他点
返回距离
static int dijkstra(){
//1.距离初始化
Arrays.fill(d,INF);
d[1]=0;
for(int i=0;i<n;i++){
//2.寻找未被标记的距离最近的点
int t=-1;
for(int j=1;j<=n;j++){
if(!st[j] && (t==-1 || d[t]>d[j]))t=j;
}
//3.标记该点
st[t]=true;
//4.用该点更新对应距离
for(int j=1;j<=n;j++){
if(d[j]>d[t]+g[t][j])d[j]=d[t]+g[t][j];
}
}
if(d[n]==INF)return -1;
return d[n];
}
算法步骤
- 距离初始化
- 源点入队(优先独立额)
- 队列非空
- 找出距离最近的点
- 标记高点
- 用该点更新其他点,并将更新的点入队
static int dijkstra(){
//1.距离初始化
Arrays.fill(d,INF);
d[1]=0;
PriorityQueue<int[]> q=new PriorityQueue<>(Comparator.comparingInt(a->a[1])); //注意这里的写法
q.offer(new int[]{1,0});//1号点距离为0;
while(!q.isEmpty()){
//2.找出距离最近的带你
int[] t=q.poll();
int index=t[0];
int dis=t[1];
//特判,入宫时已经确定的则下一次循环
if(st[index])continue;
//2.标记该点
st[index]=true;
//3.用该点更新其他点
for(int i=h[index];i!=-1;i=ne[i]){
int j=e[i];
if(d[j]>dis+w[i]){
d[j]=dis+w[i];
//如果该点被更新则加入队列
q.offer(new int[]{j,d[j]});
}
}
}
if(d[n]>INF/2)return -1;
return d[n];
}
算法步骤
- 距离初始化
- k次循环
- 备份数组
- 松弛操作
static int bellman_ford(){
//1.距离初始化
Arrays.fill(d,INF);
d[1]=0;//距离最小
for(int i=0;i<k;i++){//最多经过k条边
//这里需要注意,为了防止链式更新的出现,我们进行一次备份
back=Arrays.copyOf(d,n+1);
//进行多次松弛操作
for(int[] t:edgs){
int a=t[0];
int b=t[1];
int c=t[2];
d[b]=Math.min(d[b],back[a]+c);
}
}
return d[n];
}
算法步骤
核心思想:将距离缩短的点放到队列中,用着这些点去更新它所连接的地点
数据结构:队列(存储更新的点),st(标记对应点是不是在队列中)
- 距离初始化:INF,d[1]=0
- 将点1放入队列,
st[1]=true;
,1在队列中- 队列非空
- 弹出一个点(同时标记false)
- 用该点更新节点new,如果new 不在队列中则加入
static int spfa(){
ArrayDeque<Integer> q=new ArrayDeque<>();
Arrays.fill(d,INF);
d[1]=0;
q.offer(1);
st[1]=true;
while(!q.isEmpty()){
int t=q.poll();
st[t]=false;//不在队列里了
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
if(d[j]>d[t]+w[i]){
d[j]=d[t]+w[i];
if(!st[j]){
q.offer(j);
st[j]=true;
}
}
}
}
return d[n];
}
//1.图的初始化
// 同一个点到同一个点赋值0,其他的赋值INF
for(int i=0;i<=n;i++)Arrays.fill(g[i],INF);
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
if(i==j)g[i][j]=0;
}
}
//2.Floyd算法求解,三层循环
for(int x=1;x<=n;x++){//中介
for(int i=1;i<=n;i++){//起点
for(int j=1;j<=n;j++){//终点
g[i][j]=Math.min(g[i][j],g[i][x]+g[x][j]);
}
}
}
算法 | dijkstra | 堆dijkstra | BF | spfa |
---|---|---|---|---|
核心 | 逐个确定最近的点 | 使用堆优化找最近点的操作 | 针对边左逐步松弛操作 | 优化BF,只对距离更新的点操作 |
结构 | st:标记是否已经确定 | 堆(获取最近距离的点未被确定的),st(是否确定) | 备份数组(防止链式更新) | 队列(存储距离更新的点);st:标记是否在队列中 |
初始化 | d[i]=INF;d[1]=0 | d[i]=INF;d[1]=0; 该点放入优先队列中 | d[i]=INF;d[1]=0 | d[i]=INF;d[1]=0 ;1放入队列,标记 |
更新 | for i in 1..n 寻找最近的点 x;标记;更新 | 队列非空 ,弹出点t,标记->更新->更新的点入队 | for i in 1...n :备份数组,更新所有边 | 队列非空 ,弹出(标记)—>更新new—>new 不在则放入队列 |