最短路搜索算法

最短路搜索算法

  • 符号假设

    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)

1. 朴素版dijkstra 算法

算法流程

  1. 距离初始化:d[i]=INF,d[1]=0

  2. for i in (1,…n):

    2.1寻找集合S中距离源点最近的点,(S,未确定最短距离的点)

    2.2标记该点

    2.3用该点更新其他点

  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];
    }

2.堆优化的dijkstra

算法步骤

  1. 距离初始化
  2. 源点入队(优先独立额)
  3. 队列非空
    • 找出距离最近的点
    • 标记高点
    • 用该点更新其他点,并将更新的点入队
 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];
    }

3. Bellman_ford算法

算法步骤

  1. 距离初始化
  2. 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];
    }

4.Spfa算法

算法步骤

核心思想:将距离缩短的点放到队列中,用着这些点去更新它所连接的地点

数据结构:队列(存储更新的点),st(标记对应点是不是在队列中)

  1. 距离初始化:INF,d[1]=0
  2. 将点1放入队列,st[1]=true;,1在队列中
  3. 队列非空
    • 弹出一个点(同时标记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];
        
    }

5.Floyd算法

  • 必须使用邻接矩阵存储图
//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]);
                }
            }
        }

6.对比总结

  • 四个算法,其中两个是优化的
  • dijkstar针对点操作
  • BF,spfa针对边操作
算法 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 不在则放入队列

你可能感兴趣的:(算法)