最短路搜索算法

2023-12-31 05:58:43

最短路搜索算法

  • 符号假设

    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堆dijkstraBFspfa
核心逐个确定最近的点使用堆优化找最近点的操作针对边左逐步松弛操作优化BF,只对距离更新的点操作
结构st:标记是否已经确定堆(获取最近距离的点未被确定的),st(是否确定)备份数组(防止链式更新)队列(存储距离更新的点);st:标记是否在队列中
初始化d[i]=INF;d[1]=0d[i]=INF;d[1]=0;该点放入优先队列中d[i]=INF;d[1]=0d[i]=INF;d[1]=0;1放入队列,标记
更新for i in 1..n寻找最近的点 x;标记;更新队列非空,弹出点t,标记->更新->更新的点入队for i in 1...n:备份数组,更新所有边队列非空,弹出(标记)—>更新new—>new 不在则放入队列

文章来源:https://blog.csdn.net/weixin_45400340/article/details/135311553
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。