最短路算法

朴素dijkstra算法

适用情况:稠密图(边的数量远远多余点的数量),不能适用有负权边的图
存储方式:邻接矩阵
算法思路:进行 n 次迭代 , 每次使用未确定的最短距离的点去更新其他点。
1)每次迭代过程中都要先找到当前未确定的最短距离的点中的距离最短的点

 int t = -1;
for(int j = 1 ; j <= n ; j ++){
  if(!st[j] &&(t == -1 || dist[t] > dist[j] )
       t = j ;
}

2)用当前这个距离最短的点去去更新其他的未确定最短距离的点

for(int j  = 1 ; j <= n ; j ++ ){
   if( !st[j]  && g[t][k] != inf ){
      dist[j] = min(dist[j] , dist[t] + g[t][j])
   }
}

3)每次迭代完把该点标记为已确定

st[t] = true;

完整的代码

int dijsktra(){
  fill(dist ,inf); dist[1] = 0;
  for(int i = 1; i <= n ;i ++){
      int t = -1;
      for(int j = 1 ; j <= n ;j ++)
        if( !st[j] && (t == -1 || dist[j] < dist[t] ) t = j ;
      
      for(int j = 1 ; j <= n ;j ++ ){
        if( !st[j] && g[t][j] != inf)
            d[j] = min( d[j] , d[t] + g[t][j]);  
      }
      st[t] = true;
  }
  if(dist[n] == inf ) return -1;
  return dist[n];
}


堆优化版 dijkstra

适用情况:稀疏图,不能适用有负权边的图
存储结构:邻接表
算法思路:朴素版的dijkstra每次找当前未确定的最近的结点都要循环一遍,可以试用堆进行优化
1)初始化 dist[1] = 0 , dist[i] = inf
2)初始把1号点放入堆中。
3)当堆不为空时 :
      弹出堆顶
      用该点更新邻近的距离,如果邻近点的距离变小的,则把更新过的邻近点也加入堆中

实现代码

int heap_dijkstra(){
  boolean[] st = new boolean[n+1];
  int[] d = new int[n+1];
  fill(dist , inf); d[1] = 0;
  PriorityQueue<Pair> heap = new PriorityQueue<>( (x ,y) ->  x.dist - y.dist )
  heap.add( new Pair(1 , 0) ); //节点编号 ,距离
  while( !heap.isEmpty() ){
      Pair p = heap.poll();
      int u = p.u , dist = p.dist;
      if( st[u] ) continue;
      st[u] = true;
      for(int i = h[u] ; i != -1 ; i = ne[i] ){
            int j = e[i] ;
            if( !st[j] && d[j] > d[t] + w[i] ){
                  d[j] = d[t] + w[i];
                  heap.add(new Pair(u ,d[j]));
            } 
      }
   }
  return d[n] == inf ? -1 : d[u];
}


bellman-ford算法

适用情况:有边数限制的最短路,含有负权边,判断是否有负环
存储结构:Edge(a , b, w)的数组
算法思路:其原理为连续进行松弛,在每次松弛的时候把每条边都更新一下,若找n-1此松弛后还能更新,则说明图中有负环。
假设1号点到n号点事可达的,每一个点同时向指向的方向出发,更新相邻的点的最短距离,通过循环n-1次操作,若途中不存在负环,则1号点一定会达到n号点,若途中存在负环,则在n-1次松弛后一定还会更新。
具体步骤:

for (int i = 1 ; i <= n ;i ++){
    for (Edge :e  : edges){
        //Edge:a , b , w
        dist[b] = min( dist[b] , backup[a] + w);
      //bakcup 是上一轮迭代的备份,因为在本次迭代中 a的距离可能改变,如果使用改变了的 d[a],后续的点会受到影响。
    }  
}

在最后判断是否可达时 , 不能直接 d[n] == inf ,因为虽然1 到 n是不可达的,但是 和 n号邻近的点可能更新 d[n],所以 d[n] > inf / 2 判断 d[n]的量级

完整代码

    int ballman_ford(){
        int[] dist = new int[N];
        int[] backup = new int[N];
        Arrays.fill(dist , inf);
        dist[1] = 0;
        for(int i = 0 ; i < k ; i ++){ // 限制为最多 k 条边
            backup = Arrays.copyOf(dist , N);
            for(int j = 0 ; j < m ; j++ ){
                Edge e = edges[j];
                dist[e.b] = Math.min(dist[e.b] , backup[e.a] + e.w );
            }
        }
        return dist[n] > inf/2 ? -1 :dist[n];
    }


spfa算法

适用情况:含负权边单源最短路,判断负权环
存储结构:邻接表
算法思路:是Bellman-Ford算法的队列优化
Bellman-ford思路:
for n 次
for 所有边 a,b,w(松弛操作)
dist[b] = min(dist[b] , backup[a] + w )

spfa算法对第二hang所有边进行松弛操作进行了优化,原因是在bellman_ford算法中,即时该点的最短距离没有被更新过,但还是需要用尚未更新过的值去更新其他点,但这是没有必要的,因此只需要找到更新过的值去更新其他点即可。
spfa思路:
queue <- 1
while queue 不为空
(1) t = queue.pop()
(2) 用 t 更新所有 t的邻接点 b
queue <- b(如果该点被更新过且此时不再队列中,则加入队列)

完整代码:

    int spfa(){
        int inf = 0x3f3f3f3f;
        int[] dist = new int[N];
        boolean[] st = new boolean[N];
        int[] queue = new int[N];
        int hh = 0 , tt = -1;
        Arrays.fill(dist , inf); dist[1] = 0;
        queue[++tt] = 1;
        while(hh <= tt){
            int t = queue[hh++];
            st[t] = false;
            for(int i = h[t] ; i != -1 ; i = ne[i] ){
                int j = e[i];
                if( dist[j] > dist[t] + w[i] ){
                    dist[j] = dist[t] + w[i];
                    if(!st[j]){
                      queue[++tt] = j;
                      st[j] = true;
                    } 
                }
            }
        }
        return dist[n] == inf ? -1 : dist[n];
     }

spfa判负环思路:
(1)统计每个带你入队次数,如果某个点入队n次,则说明存在负环
(2)统计当前每个点的最短路中包含的边数,如果某点的最短路包含的边数大于等于n,则也说明存在环

实现代码

    boolean spfa(){
        //int inf = 0x3f3f3f3f;
        int[] dist = new int[N];
       // Arrays.fill(dist , inf); 这里不用是因为如果有负环 0 > 0 + 负权边
        boolean[] st = new boolean[N];
        Queue<Integer> queue = new LinkedList<>();
        for(int i = 1 ; i <= n ;i ++){
              queue.add(i);
              st[i] = true;
        }
        int[] cnt = new int[N];
        while(!queue.isEmpty()){
            int t = queue.poll();
            st[t] = false;
            for(int i = h[t] ; i != -1 ; i = ne[i]){
                int j = e[i];
                if( dist[j] > dist[t] + w[i] ){
                    dist[j] = dist[t] + w[i];
                    cnt[j] = cnt[t] + 1;
                    if(cnt[j] >= n) return true;
                    if( !st[j] ){
                        queue.add(j);
                        st[j] = true;
                    }
                }
            }
        }
        return false;    
    }


floyd算法

适用场景:多源最短路
存储结构:邻接矩阵
算法思路:枚举 每一个结点 , 把该结点作为中间点 ,看其他经过该点的路径长度是否能够变短

实现代码

    void floyd(){
         for(int k = 1 ; k <= n ;k ++)
            for(int i = 1 ;i <= n ;i ++)
                for(int j = 1 ; j <= n ; j++)
                    g[i][j] = Math.min( g[i][j] , g[i][k] + g[k][j] );
image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容