最短路径

图的最短路径

只是个人的总结, 防止忘记

定义: 找到一个点到另一个顶点成本最小的路径

Dijkstra( 权重非负, 有无环都可)

能够得到最短路径的算法, 只要证明自己可以放松所有的边, 直到都失效为止.

对于朴素算法: 需要最终所有节点都被放松过

对于有限队列优化的: 优先级最小的顶点的distTo[]值就是最短路径的权重, 它不会小于任意已经放松过的任意顶点的最短路径的权重, 也不会大于任何还未被放松过的任意顶点的最短路径的权重. 这样, 所有s可以到达的顶点的最短路径都按照最短路径的权重顺序被放松.

如果没有环, 那可以用拓扑排序优化, 先拓扑排序, 然后按照顺序放松节点, 这是无环最优化的, 且可以处理负权重边.

可以做的leetcode题目

(https://leetcode-cn.com/problems/path-with-maximum-probability/)

(https://leetcode-cn.com/problems/path-with-minimum-effort/)

(https://leetcode-cn.com/problems/network-delay-time/)

Dijkstra像是 BFS 和 PRIM 算法.

朴素的Dijkstra:

会放松所有的边, ans里的节点都会被访问到. 且可以看到一个节点的距离值可以被多次修改.

 // 3. dijkstra
  def dijkstraSolution(times: Array[Array[Int]], n:Int, k:Int): Int = {
    import scala.collection.mutable
    val tu = mutable.Map[Int, mutable.Map[Int, Int]]()
    (1 to n).foreach{ i => tu.put(i, mutable.Map[Int, Int]())}
    times.foreach{
      case Array(uid, vid, weight) => {
        tu(uid).put(vid, weight)
      }
    }
    val ans = mutable.Map[Int, Int]((1 to n).map( _ -> Integer.MAX_VALUE):_*) // 这个, ans的元素综合为n个和88行, 保证了全部能被访问到
    ans.update(k, 0)

    val visitedSet = mutable.Set[Int]()
    while(visitedSet.size != n) {
      val _@(miniNode, miniWeight): (Int, Int) = ans.filterNot(e => visitedSet.contains(e._1)).minBy(_._2)
      visitedSet.add(miniNode)
        //   println("visited: " + visitedSet.map(_.toString))
        //   println("ans" + ans.toMap)
      val adjs: mutable.Map[Int, Int] = tu(miniNode)
      adjs.foreach{
        case (adjNode, adjValue) => {
          ans(adjNode) =  math.min(adjValue + miniWeight,  ans(adjNode))
        //   ans.update(adjNode, math.min(adjValue + miniWeight,  ans(adjNode)))
        }

      }
    }

    if(ans.values.exists( _ == Integer.MAX_VALUE)) {
      -1
    }else{
      ans.values.max
    }

  }

优先队列优化的

在设置一个点的距离前, 会将所有之前放松其他边得到的可能最小距离值, 放到优先队列中.

另外, 优先队列里的元素, 是修改它的值, 还是重新压入,使得队列中存在失效的数据. 这两种方式, 前者是即时实现, 后者是lazy的延时实现.(我没太明白算法4里的这个)

  // 4. optimized dijstra
  def optimized_dijstra(times: Array[Array[Int]], n: Int, k: Int) = {
    import scala.collection.mutable
    val tu = mutable.Map[Int, mutable.Map[Int, Int]]()
    (1 to n).foreach{ i => tu.put(i, mutable.Map[Int, Int]())}
    times.foreach{
      case Array(uid, vid, weight) => {
        tu(uid).put(vid, weight)
      }
    }
    val ans = mutable.Map[Int, Int]((1 to n).map( _ -> Integer.MAX_VALUE):_*)
    implicit val tupleOrdering: Ordering[(Int, Int)] = new Ordering[(Int, Int)]{
      override def compare(x: (Int, Int), y: (Int, Int)): Int = x._1 - x._2
    }
    val updateSignalPriorityQueue = mutable.PriorityQueue[(Int, Int)]()(tupleOrdering)
    updateSignalPriorityQueue.enqueue((k,0))
      
    while(updateSignalPriorityQueue.nonEmpty) { // method1

      val _@(miniNode, updatedWeight) = updateSignalPriorityQueue.dequeue()

      // 如果没有被更新过...
      // 被更新过
      if(ans(miniNode) == Integer.MAX_VALUE || updatedWeight < ans(miniNode)) { 
    //  if( ans(miniNode) == Integer.MAX_VALUE || !visitedSet.contains(miniNode)) {
        ans(miniNode) = updatedWeight
        // visitedSet.add(miniNode)
        tu(miniNode).foreach {
          case _@(adjV, adjW) => {
            updateSignalPriorityQueue.enqueue(adjV -> (updatedWeight + adjW))  // 这个方法的复杂度和对有限队列的操作成正比, updateWeight < ans(miniNOde)保证了失效的边不会再作用, 但也是个弹出操作, 而如果在
                                                                              // 这里, 不用enqueue, 如果adjV存在于有限队列,修改它的值,并且,调整顺序, 总调整次数与弹出操作次数相同
          }
        }
      }else{
            ()
      }
    }
    if(ans.values.exists( _ == Integer.MAX_VALUE)) {
      -1
    }else{
      ans.values.max
    }
  }

确定无环的用拓扑排序优化

bfs 或者 dfs 的方式

效率不高 , 可以忽略

变种

最长路径
  1. 转换视角, 非负权重图的最短路径就是 负权重值的最长路径 , 只要把原图的权重全部取反, 最后得到结果后再取反就好.
  2. 改变relax函数中的不等式方向, 原来是新的权重如果小于旧的权重更新, 现在改成, 新的权重如果大于旧的则更新

两种方法都是转化了视角, 最短和最长是一对对偶.

另外, 在加权有向图(权重可能为负数) 中寻找最长路径, 已知最好的算法的复杂度也是指数级别的, 而若是有环, 则更复杂了.

并行任务调度

优先级限制下的并行任务调度定义:

  1. 一组需要完成的任务和每个任务所需时间
  2. 一组关于任务完成的先后次序的优先级限制

在满足此限制条件下, 在数量不限的处理器上安排这些任务, 使得任务最快完成, 且使用的处理器资源最少(可以理解为cpu不要忙等, 即给定任务要直接执行, 而不是还要再等它的前序先完成)最少.

这个问题, 可以通过"关键路径"的方法证明它和 "无环加权有向图"的最长路径 问题等价

将定义里的两个条件转换成对应的图:

image.png
image.png

将任务转化成图的一条边, 边有两个点, 任务的时间就是边的权重, 而 "关键路径" 即条件二: 优先级次序 转化而来的, 关键路径之间转化的节点间也有路径, 不过权重为0 , 此外, 开始S具有0权重到任意任务的开始节点, 任意任务的结束节点有0权重到结束节点. 第二章图中的最长路径, 就是每个路径的开始节点的最长路径权重值就是它的任务开始时间.

相对最后期限下的并行任务调度

和上个情况不同的是, 多了一个限制类型: 某个任务需要在指定的时间点之前开始, 即指定和另一个任务的开始时间的相对时间. 举例: 任务2 必须在任务4 开始后的12个时间内启动.

等价为; 加权有向图(可能环以及负权重边)的最短路径问题

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

推荐阅读更多精彩内容