fucking-algorithm icon indicating copy to clipboard operation
fucking-algorithm copied to clipboard

我写了一个模板,把 Dijkstra 算法变成了默写题 :: labuladong的算法小抄

Open utterances-bot opened this issue 4 years ago • 38 comments

文章链接点这里:我写了一个模板,把 Dijkstra 算法变成了默写题

评论礼仪 见这里,违者直接拉黑。

utterances-bot avatar Oct 13 '21 08:10 utterances-bot

1514题可以看作求失败概率最小的路径,理解起来会更容易

hyzhou404 avatar Oct 13 '21 08:10 hyzhou404

   while (!pq.isEmpty()) {
    State curState = pq.poll();

    int curNodeID = curState.id;
    int curDistFromStart = curState.distFromStart;

    // 在这里加一个判断就行了,其他代码不用改
    if (curNodeID == end) {
        return curDistFromStart;
    }

    if (curDistFromStart > distTo[curNodeID]) {
        continue;
    }
   }

"因为优先级队列自动排序的性质,每次从队列里面拿出来的都是 distFromStart 值最小的,所以当你从队头拿出一个节点,如果发现这个节点就是终点 end,那么 distFromStart 对应的值就是从 start 到 end 的最短距离。"

 以上判断好像有问题 例如: a->b->c 权重分别为2,1 , a到c的路径最小距离是3 a->d->e->f->c 权重分别为1, a到c的路径最小距离是4 按照你上边所说, 应该会遍历a->d->e->f->c 这条路径, 拿到的最小路径是4, 是错误的

doubleSkinMilk avatar Dec 03 '21 05:12 doubleSkinMilk

re: doubleSkinMilk 在你举得例子中 b->c 会先于 f->c 被遍历到,因为distFromStart是一个Priority Queue,而distFromStart[b] < distFromStart[f]

YZG112358 avatar Dec 23 '21 19:12 YZG112358

东哥好,大家好。我有个问题没想明白。在第 743 题「网络延迟时间」中,为什么curDistFromStart > distTo[curId]。也就是当前路径长度小于dp table里储存的路径长度时,不去更新distTo[curId]啊。 就是为什么不用写注释掉的那一行。

    int[] dijkstra(int start, List<int[]>[] graph){
        int[] distTo = new int[graph.length];
        Arrays.fill(distTo, Integer.MAX_VALUE);
        distTo[start] = 0;

        PriorityQueue<State> pq = new PriorityQueue<>(
            graph.length, (a,b) ->(a.distFromStart - b.distFromStart)
        );
        pq.offer(new State(start,0));

        while(!pq.isEmpty()){
            State curState = pq.poll();
            int curId = curState.id;
            int curDistFromStart = curState.distFromStart;

            if(curDistFromStart > distTo[curId]){
                continue;
            }else{
                // distTo[curId] = curDistFromStart;
                for(int[] nextNode : graph[curId]){
                    int nextId = nextNode[0];
                    int nextDistFromStart = distTo[curId] + nextNode[1];
                    if(nextDistFromStart < distTo[nextId]){
                        distTo[nextId] = nextDistFromStart;
                        pq.offer(new State(nextId, nextDistFromStart));
                    }
                }
            }
        }
        return distTo;
    }

JackShang94 avatar Dec 30 '21 04:12 JackShang94

我刚才试了下,加上 distTo[curId] = curDistFromStart;对结果没有影响。我能想明白加上的逻辑,但想不明白为什么去掉后算法还是对的呢?

JackShang94 avatar Dec 30 '21 04:12 JackShang94

@doubleSkinMilk 仔细想一下确实有问题,不知道东哥能不能出来解释下?

southrivers avatar Jan 02 '22 14:01 southrivers

@YZG112358 "在你举得例子中 b->c 会先于 f->c 被遍历到,因为distFromStart是一个Priority Queue,而distFromStart[b] < distFromStart[f]",理论上来说f在b后面是没有问题的,关键有可能还没有到b呢f就先到了终点c,我还是很难理解这种思路

southrivers avatar Jan 04 '22 17:01 southrivers

@doubleSkinMilk @southrivers 首先有几个算法里的前置条件

  1. 每一次移动都会导致路径的权重增加,即路径权重大于等于0
  2. 每次在PriorityQueue里取出的node(i)是已经到达的点,而不是即将到达
  3. 每次在PriorityQueue里取出的node(i),不管这个node(i)是不是离目标地最近,至少是从起点到node的路线里最短的
  4. 接着上一点,相比于node(i), 在PriorityQueue里面的node(j), node(k)等等,他们到达各自当前的点的距离必然是大于等于node(i)的权重

由此可以推断

  • 根据(2)和(3),当PriorityQueue里取出的node(destination)的时候,我们至少得到了一种到达终点的路径path(i)和这条路径的权重weight(i)
  • 根据(4),目前得到的这个路径path(i)的权重weight(i)是小于等于PriorityQueue中其他还没到达终点的路径
  • 根据(1),其他的路径到达终点时因为还有走更多的路,所以必然大于等于他们当前的权重,除非其他的路径也正好到达了终点。所以,其他的备选路径的最终权重大于等于当前权重
  • 结合前两点,路径path(i)到达终点的权重小于等于其他路径的最终权重

用公式表达

  • 因为PriorityQueue对权重排序
    • weight(i) <= weight(j), weight(k) ...
  • 因为path(i)已经到达终点
    • weight(destination, i) = weight(i)
    • weight(destination, i) <= weight(j), weight(k), ...
  • 因为路径权重不为负数,其他路径的最终权重会大于当前权重。除非已经到达终点或者剩余路程权重都是0
    • weight(j) <= weight(destination, j)
    • weight(k) <= weight(destination, k)
    • ...
  • 根据不等式传递公式
    • weight(destination, i) = weight(i) <= weight(j), weight(k), ... <= weight(destination, j), weight(destination, k), ...

Goolloo avatar Jan 07 '22 09:01 Goolloo

@JackShang94 在加入PirorityQueue的时候已经更新过了。类似的还有BFS模版里在加入Queue的时候同时加入visited。

                for(int[] nextNode : graph[curId]){
                    int nextId = nextNode[0];
                    int nextDistFromStart = distTo[curId] + nextNode[1];
                    if(nextDistFromStart < distTo[nextId]){
                        //******************************************************
                        distTo[nextId] = nextDistFromStart;
                        //******************************************************
                        pq.offer(new State(nextId, nextDistFromStart));
                    }
                }

Goolloo avatar Jan 07 '22 09:01 Goolloo

@Goolloo,我有相同的疑问,请问为什么这个地方nextDistFromStart = distTo[curId] + nextNode[1];这么更新之后再加入PriorityQueue也是对的呢?这个时候distTo[curId] >= curDistFromStart,不应该先更新distTo[curId] = curDistFromStart吗?

XiaoyuZhou-hub avatar Jan 08 '22 13:01 XiaoyuZhou-hub

@JackShang94 @XiaoyuZhou-hub 关于你们的疑问, @Goolloo 说的是对的,入队的时候已经。你可以先不考虑 Dijkstra 算法,就考虑 常规 BFS 算法,也是要在入队之前把节点加入 visited 集合,而不是在出队的时候加入 visited 集合。

至于为什么要在入队的时候加入 visited 集合,主要是效率的考量:

由于可能多次访问同一节点(走回头路),所以在一个节点 x 入队之后但还未出队的这段时间窗口里,BFS 算法还可能会访问 x 试图将它加入队列。如果在 x 入队的时候就加入 visited,那么这种情况就不会发生;但如果等 x 出队后才加入 visited,这种情况就会发生,导致队列中存在很多冗余重复的 x,它们不会影响最终结果的正确性,但是会拖慢算法的效率。

Dijkstra 算法作为进阶版的 BFS 算法,也是同样的道理,distTo 数组起到的作用就类似 visited 集合的作用。

@JackShang94 你在那里加代码不会产生任何影响,因为 distTo[curId] 那时候本就等于 curDistFromStart,它们就是上一轮的 distTo[nextId]nextDistFromStart

进一步,你甚至可以尝试把 distTo[nextId] = nextDistFromStart; 这一句代码去掉,结果应该也是对的,但是效率会大幅下降。这就是我刚说的 BFS 算法中加入 visited 集合的时机的问题了。

labuladong avatar Jan 09 '22 04:01 labuladong

@doubleSkinMilk @southrivers 你们说的不对,可能对算法的执行过程还是不熟悉,不妨自己画个图手动执行一下算法逻辑就理解了。

以你们说的例子,节点 bdistFromStart 是 2,节点 fdistFromStart 是 3,这时候优先级队列会优先走 b,也就走到 c 了,得到最小路径 a -> b -> c

labuladong avatar Jan 09 '22 04:01 labuladong

@apache1011, 第三题确实感觉转换成计算最小的failure_rate再结合东哥的模板和思路会更加好理解.

import heapq

class Solution:
    def _build_graph(self, n, edges, succ_probs):
        graph = [[] for _ in range(n)]
        
        for edge, succ_prob in zip(edges, succ_probs):
            graph[edge[0]].append([edge[1], succ_prob])
            graph[edge[1]].append([edge[0], succ_prob])
            
        return graph
    
    def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start: int, end: int) -> float:
        # build the graph for N nodes - by using edges and succ_prob lists
        graph = self._build_graph(n, edges, succProb)
        
        # set max value as place holder to help to calc the smaller failure rates
        path_probs = [float('inf')] * n
        
        # init failure rate should be 0
        path_probs[start] = 0
        pq = [State(start, 0)]
        
        while pq:
            curr_node = heapq.heappop(pq)
            curr_id = curr_node.id
            curr_prob = curr_node.prob
            
            if curr_id == end:
                # return 1 - the smallest_failure_rate
                return 1 - curr_prob
            
            # the current failure rate has been the smaller one
            if path_probs[curr_id] < curr_prob:
                continue
            
            for nei_id, nei_prob in graph[curr_id]:
                # 1 - (1 - failure_rate) * success_rate = evolved_failure_rate
                nxt_node_prob = 1 - (1-path_probs[curr_id]) * nei_prob
                if path_probs[nei_id] > nxt_node_prob:
                    path_probs[nei_id] = nxt_node_prob 
                    heapq.heappush(pq, State(nei_id, nxt_node_prob))
        
        return 0
                

class State:
    
    def __init__(self, id, prob):
        self.id = id
        self.prob = prob
    
    def __lt__(self, other):
        # track the failure rate - needs to return smaller one
        # so that 1 - smallest_failure_rate = max_success_rate
        return self.prob < other.prob

JackHo327 avatar Jan 11 '22 06:01 JackHo327

东哥 捉个插件虫 1514的思路是1631的

ireneli0 avatar Jan 12 '22 19:01 ireneli0

@ireneli0 感谢指出,已修复

labuladong avatar Jan 13 '22 12:01 labuladong

js 版 dijkstra 模板

function dijkstra(start, /* end */) {
    // 图中节点的个数
    const n = graph.length;
    // 记录最短路径的权重,你可以理解为 dp table
    // 定义:distTo[i] 的值就是节点 start 到达节点 i 的最短路径权重
    // 求最小值,所以初始化为正无穷
    const distTo = new Array(n).fill(Infinity);
    // base case,start 到 start 的最短距离就是 0
    distTo[start] = 0;

    // 优先级队列,dist 较小的排在前面
    const q = new PriorityQueue((a, b) => a[1] < b[1]);
    // 从起点 start 开始进行 BFS
    q.push([start, 0]);

    while (!q.isEmpty()) {
      const [v, dist] = q.pop();

      // // 如果只要到 end, 在这里加一个判断就行了,其他代码不用改
      // if (v === end) {
      //     return dist;
      // }

      if (dist > distTo[v]) {
        // 已经有一条更短的路径到达 v 节点了
        continue;
      }

      // 将相邻节点装入队列
      for (const n of graph[v]) {
        let distToN = distTo[v] + weight(v, n);
        // 看看从 v 达到 n 的距离是否会更短
        if (distToN < distTo[n]) {
          // 更新 dp table
          distTo[n] = distToN;
          q.push([n, distToN]);
        }
      }
    }

    // // 如果只要到 end, 如果运行到这里,说明从 start 无法走到 end
    // return Infinity

    return distTo;
  }

jswxwxf avatar Feb 15 '22 06:02 jswxwxf

可以运行的 js 版 743. 网络延迟时间

var networkDelayTime = function(times, n, k) {
  const graph = buildGraph();
  const distTo = dijkstra(k);
  // 找到最长的那一条最短路径
  let result = 0;
  for (let i = 1; i < distTo.length; i++) {
    if (distTo[i] === Infinity) {
      return -1;
    }
    result = Math.max(result, distTo[i]);
  }
  return result;

  function buildGraph() {
    // 节点编号是从 1 开始的,所以要一个大小为 n + 1 的邻接表
    const graph = new Array(n + 1).fill(0).map(() => []);
    // 构造图
    for (const [from, to, weight] of times) {
      // 邻接表存储图结构,同时存储权重信息
      graph[from].push([to, weight]);
    }
    return graph;
  }
  
  function dijkstra(start) {
    // 图中节点的个数
    const n = graph.length;
    // 记录最短路径的权重,你可以理解为 dp table
    // 定义:distTo[i] 的值就是节点 start 到达节点 i 的最短路径权重
    // 求最小值,所以初始化为正无穷
    const distTo = new Array(n).fill(Infinity);
    // base case,start 到 start 的最短距离就是 0
    distTo[start] = 0;

    // 优先级队列,dist 较小的排在前面
    const q = new PriorityQueue((a, b) => a[1] < b[1]);
    // 从起点 start 开始进行 BFS
    q.push([start, 0]);

    while (!q.isEmpty()) {
      const [v, dist] = q.pop();

      // // 在这里加一个判断就行了,其他代码不用改
      // if (v === end) {
      //     return dist;
      // }

      if (dist > distTo[v]) {
        // 已经有一条更短的路径到达 v 节点了
        continue;
      }

      // 将相邻节点装入队列
      for (const [n, weight] of graph[v]) {
        let distToN = distTo[v] + weight;
        // 看看从 v 达到 n 的距离是否会更短
        if (distToN < distTo[n]) {
          // 更新 dp table
          distTo[n] = distToN;
          q.push([n, distToN]);
        }
      }
    }

    // // 如果运行到这里,说明从 start 无法走到 end
    // return Infinity

    return distTo;
  }

};

class PriorityQueue {
  constructor(comparator = (a, b) => a > b) {
    this._heap = [];
    this._comparator = comparator;
  }

  size() {
    return this._heap.length;
  }

  isEmpty() {
    return this.size() === 0;
  }

  peek() {
    return this._heap[0];
  }

  _parent(idx) {
    return Math.floor((idx - 1) / 2);
  }

  _leftChild(index) {
    return index * 2 + 1;
  }

  _rightChild(index) {
    return index * 2 + 2;
  }

  _swap(i, j) {
    [this._heap[i], this._heap[j]] = [this._heap[j], this._heap[i]];
  }

  _compare(i, j) {
    return this._comparator(this._heap[i], this._heap[j]);
  }

  push(value) {
    this._heap.push(value);
    this._shifUp();
    return this.size();
  }

  _shifUp() {
    let idx = this.size() - 1;
    while (idx > 0) {
      const parent = this._parent(idx);
      if (this._compare(idx, parent)) {
        this._swap(idx, parent);
      } else {
        break;
      }
      idx = parent;
    }
  }

  pop() {
    let top = this.peek();
    this._heap[0] = this._heap[this.size() - 1];
    this._heap.length = this._heap.length - 1;
    this._siftDown();
    return top;
  }

  _siftDown() {
    let idx = 0;
    while (idx < this.size()) {
      let leftIdx = this._leftChild(idx);
      if (leftIdx >= this.size()) break;
      let rightIdx = this._rightChild(idx);
      let targetIdx = leftIdx;
      if (rightIdx < this.size()) {
        targetIdx = this._compare(leftIdx, rightIdx) ? leftIdx : rightIdx;
      }
      if (this._compare(targetIdx, idx)) {
        this._swap(targetIdx, idx);
      } else {
        break;
      }
      idx = targetIdx;
    }
  }
}

jswxwxf avatar Feb 15 '22 06:02 jswxwxf

        if (curDistFromStart > distTo[curNodeID]) {
            // 已经有一条更短的路径到达 curNode 节点了
            continue;
        }

lancerts avatar Mar 02 '22 23:03 lancerts

如果觉得confused的话 其实可以去掉

剪枝逻辑如果去掉,效率就大幅降低了

labuladong avatar Mar 04 '22 02:03 labuladong

2203 是道扩展题,需要用Dijkstra求三个点到其他点的距离。

Herts avatar Mar 13 '22 04:03 Herts

  1. 网络延迟时间(中等) python version
import collections
import heapq

class State(object):
    def __init__(self, id, dist_from_start):
        self.id = id
        self.dist_from_start = dist_from_start

    def __lt__(self, other):
        if self.dist_from_start < other.dist_from_start:
            return True
        return False


def dijkstra(graph, start):
    n = len(graph)
    dist_to = [float("inf")] * (n + 1)
    # base case:start to start
    dist_to[start] = 0
    pq = []
    heapq.heappush(pq, State(start, 0))
    while pq:
        cur_state = heapq.heappop(pq)
        cur_node_id, cur_node_dist_from_start = cur_state.id, cur_state.dist_from_start
        # 已经有一条更短的路径到达cur_node节点
        if cur_node_dist_from_start > dist_to[cur_node_id]:
            continue
        for next_node in graph[cur_node_id]:
            # 判断是否前进到下个节点
            next_node_id, weight = next_node
            dist_to_next_node = dist_to[cur_node_id] + weight
            if dist_to_next_node < dist_to[next_node_id]:
                dist_to[next_node_id] = dist_to_next_node
                heapq.heappush(pq, State(next_node_id, dist_to_next_node))
    return dist_to


class Solution:
    def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int:
        # 构建图
        graph = collections.defaultdict(list)
        for i in range(1, n + 1):
            graph[i] = []
        for time in times:
            _from, _to, weight = time
            graph[_from].append((_to, weight))

        dist_to = dijkstra(graph, k)

        res = 0
        for i in range(1, len(dist_to)):
            if dist_to[i] == float("inf"):
                return -1
            res = max(res, dist_to[i])
        return res

Alwin4Zhang avatar Mar 29 '22 03:03 Alwin4Zhang

1631 题「 最小体力消耗路径」python version

import collections
import heapq

class State(object):
    def __init__(self, x, y, effort_from_start):
        self.x = x
        self.y = y
        self.effort_from_start = effort_from_start

    def __lt__(self, other):
        return self.effort_from_start < other.effort_from_start


dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)]


def adj(matrix, x, y):
    # 返回坐标(x,y)的上下左右相邻坐标
    m, n = len(matrix), len(matrix[0])
    # 存储相邻节点
    neighbors = []
    for _dir in dirs:
        nx = x + _dir[0]
        ny = y + _dir[1]
        if nx >= m or nx < 0 or ny >= n or ny < 0:  # 索引越界
            continue
        neighbors.append((nx, ny))
    return neighbors


class Solution:
    def minimumEffortPath(self, heights: List[List[int]]) -> int:
        m, n = len(heights), len(heights[0])
        # 定义: 从(0,0)到(i,j)的最小体力消耗是effort_to[i][j]
        effort_to = [[float("inf")] * n for _ in range(m)]
        # base case 从自己到自己就是0
        effort_to[0][0] = 0
        pq = []
        heapq.heappush(pq, State(0, 0, 0))
        while pq:
            cur_state = heapq.heappop(pq)
            cur_x, cur_y, cur_effort_from_start = cur_state.x, cur_state.y, cur_state.effort_from_start
            if cur_x == m - 1 and cur_y == n - 1:
                return cur_effort_from_start
            if cur_effort_from_start > effort_to[cur_x][cur_y]:
                continue
            for neighbor in adj(heights, cur_x, cur_y):
                next_x, next_y = neighbor[0], neighbor[1]
                # 计算从(cur_x,cur_y)达到(next_x,next_y)的消耗
                effort_to_next_node = max(effort_to[cur_x][cur_y], abs(
                    heights[cur_x][cur_y] - heights[next_x][next_y]))
                # 更新dp table
                if effort_to[next_x][next_y] > effort_to_next_node:
                    effort_to[next_x][next_y] = effort_to_next_node
                    heapq.heappush(
                        pq, State(next_x, next_y, effort_to_next_node))
        return -1

Alwin4Zhang avatar Mar 29 '22 07:03 Alwin4Zhang

1514 题「 概率最大的路径」 python version

import heapq
import collections

class State(object):
    def __init__(self, id, prob_from_start):
        self.id = id
        self.prob_from_start = prob_from_start

    def __lt__(self, other):  # 大顶堆
        return self.prob_from_start > other.prob_from_start


class Solution:
    def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start: int, end: int) -> float:
        graph = collections.defaultdict(list)
        for i in range(n):
            graph[i] = []
        # 构建图 无向图等价于双向图    
        for i in range(len(edges)):
            _from,_to,weight = edges[i][0],edges[i][1],succProb[i]
            graph[_from].append((_to, weight))
            graph[_to].append((_from, weight))

        prob_to = [-1] * n
        # base case
        prob_to[start] = 1
        pq = []
        heapq.heappush(pq, State(start, 1))
        while pq:
            cur_node = heapq.heappop(pq)
            cur_node_id, cur_prob_from_start = cur_node.id, cur_node.prob_from_start
            if cur_node_id == end:
                return cur_prob_from_start
            if cur_prob_from_start < prob_to[cur_node_id]:
                # 如果当前节点的累积已经比之前的小,直接跳过(再乘只会越来越小)
                continue
            for neighbor in graph[cur_node_id]:
                next_node, weight = neighbor
                next_node_prob_from_start = prob_to[cur_node_id] * weight
                if next_node_prob_from_start > prob_to[next_node]:
                    prob_to[next_node] = next_node_prob_from_start
                    heapq.heappush(
                        pq, State(next_node, next_node_prob_from_start))
        return 0.0

Alwin4Zhang avatar Mar 29 '22 08:03 Alwin4Zhang

关于@JackShang94和@XiaoyuZhou-hub的疑问,我认为出现这样的疑问的原因是:代码的枝剪部分规定只有当curDistFromStart <= distTo[curNodeID]时才会继续后面的for loop。这么看来curDistFromStart似乎有可能会小于distTo[curNodeID],而for loop中计算distToNextNode时用的却是可能比curDistFromStart更大的distTo[curNodeID](衍生而出的问题就是既然distTo[curNodeID]可能比curDistFromStart大,那么为什么不先更新distTo[curNodeID]的值?)。

...
        if (curDistFromStart > distTo[curNodeID]) {
            // 已经有一条更短的路径到达 curNode 节点了
            continue;
        }
        // 将 curNode 的相邻节点装入队列
        for (int nextNodeID : adj(curNodeID)) {
            // 看看从 curNode 达到 nextNode 的距离是否会更短
            int distToNextNode = distTo[curNodeID] + weight(curNodeID, nextNodeID);
...

那么问题实际上是什么时候会出现curDistFromStart > distTo[curNodeID]?什么时候会出现curDistFromStart < distTo[curNodeID]

其实trace一遍distTopq中元素值的变化就可以明白,以文中的下图为例:

首先要说明下东哥的代码是在节点入队pq之前更新distTo数组,而不是在出队时更新。 当while loop循环完第3轮后distTo[5]的值为10,而pq内的元素为[[1, 9], [5, 10], [5, 11]](格式是{nodeID, distFromStart})。可以看到在[5, 10]入队的同时,distTo[5]的值也由11更新为10,同时pq中会同时存在[5, 10][5, 11]

什么时候会存在curDistFromStart > distTo[curNodeID]

[5, 11]出队时,由于此时distTo[5]的值为10,便不再进入后续for loop。因为distTo[5]的值没有更新,那么就算进入后续的for loop,与5号节点相邻节点的distTo的值也不会更新。

什么时候会存在curDistFromStart < distTo[curNodeID]?为什么要用distTo[curNodeID]来计算distToNextNode

事实上curDistFromStart并不会小于distTo[curNodeID]。前面提到过,distTo内的值都是在节点入队之前更新的,也就是说是先更新的distTo再将节点入队。而curDistFromStart是在节点出队时得到的,也就是说curDistFromStart只会大于distTo[curNodeID]curDistFromStart入队之后distTo[curNodeID]被更小的值更新了)或者等于distTo[curNodeID]curDistFromStart入队之后distTo[curNodeID]没被更新)。

使用distTo[curNodeID]来计算distToNextNode的原因我认为是在定义上distTo中存储的是节点到起点的最短距离。虽然由于枝剪代码的存在,当计算distToNextNode时,curDistFromStart只可能等于distTo[curNodeID](如之前所述,本来就不存在小于的情况,枝剪代码又去掉了大于的情况),但是根据算法最初的定义,起点到邻居节点的最短距离=起点到当前节点的最短距离+当前节点到邻居节点的距离,所以distTo[curNodeID]才是计算时应该使用的变量。而distTo[curNodeID]不可能大于curDistFromStart由于是一个隐藏条件,结合枝剪代码,可能造成了一些误解。

mingweiarthurli avatar Apr 03 '22 05:04 mingweiarthurli

@mingweiarthurli 给你点赞,写的很详细,这个算法的细节确实容易绕不清楚,我说下我的理解:

distTo[id] 永远维护到达 id 节点的最小距离,而 State 里面那个 distFromStart,维护的是 State.id 这个节点入队时距离起点的距离;该 State 会在队列中停留一段时间,等它被再次拿出队列的时候,distTo[State.id] 可能已经被更新成更小的值了。

比如这幅图,你手动执行一下算法:

节点 5 第一次入队时 State 记录的 distFromStart 是 11,但等这个 State 出队时,distTo[5] 已经更新成了 10,所以当然需要被这段逻辑 continue 掉:

if (curDistFromStart > distTo[curNodeID]) {
    // 已经有一条更短的路径到达 curNode 节点了
    continue;
}

labuladong avatar Apr 04 '22 09:04 labuladong

请问如果是求无向无环图中任意两个节点间的最长路径,该怎么做呢?

Edwardmark avatar May 11 '22 08:05 Edwardmark

无向无环图你把它看成双向的有向就可以了 区别只在于构建图的时候

cedricchan avatar May 14 '22 23:05 cedricchan

东哥,请问可以给一个C++的模板吗?

YCHang686 avatar May 16 '22 02:05 YCHang686

注意: Dijkstra 算法中构建的图因为存在权重,所以构建图时 存储的内容是 (即 : 权重+结束点

zhongweiLeex avatar Jun 01 '22 14:06 zhongweiLeex

有一个问题,用priorityqueue复杂度是O(ElogE),那用linkedlist是不是就只有O(E)了?虽然linkedlist更慢但是worst case却更好?

JerryWuZiJie avatar Jun 29 '22 04:06 JerryWuZiJie