直接 Dijkstra 算法的时间复杂度

Posted

技术标签:

【中文标题】直接 Dijkstra 算法的时间复杂度【英文标题】:Time Complexity of Straight Forward Dijkstra's Algorithm 【发布时间】:2020-04-15 03:27:05 【问题描述】:

我很难看到直接实现 Dijkstra 算法(没有堆)的 O(mn) 界限。在我的实现和其他实现中,我发现主循环迭代 n-1 次(对于不是源的每个顶点,n-1),然后在每次迭代中找到最小顶点是 O(n)(检查队列中的每个顶点并找到到源的最小距离),然后每个发现的最小顶点最多有 n-1 个邻居,因此更新所有邻居是 O(n)。在我看来,这会导致 O(n^2) 的界限。下面提供了我的实现

public int[] dijkstra(int s) 
      int[] dist = new int[vNum];
      LinkedList queue = new LinkedList<Integer>();
      for (int i = 0; i < vNum; i++) 
         queue.add(i); // add all vertices to the queue
         dist[i] = Integer.MAX_VALUE; // set all initial shortest paths to max INT value
      
      dist[s] = 0; // the source is 0 away from itself

      while (!queue.isEmpty())  // iterates over n - 1 vertices, O(n)

         int minV = getMinDist(queue, dist); // get vertex with minimum distance from source, O(n)
         queue.remove(Integer.valueOf(minV)); // remove Integer object, not position at integer

         for (int neighbor : adjList[minV])  // O(n), max n edges
            int shortestPath = dist[minV] + edgeLenghts[minV][neighbor];
            if (shortestPath < dist[neighbor]) 
               dist[neighbor] = shortestPath; // a new shortest path have been found
            
         
      

      return dist;

   

我不认为这是正确的,但我很难看到 m 因素在哪里。

【问题讨论】:

【参考方案1】:

您的实现确实消除了 M 因素,至少如果我们只考虑简单的图(两个顶点之间没有多重边)。是 O(N^2)!如果您遍历所有可能的边而不是顶点,复杂度将为 O(N*M)。

编辑:嗯,更具体地说,实际上是 O(M + N^2)。在您的算法中更改某些顶点中的值需要 O(1) 时间,并且每次考虑边时都可能发生,换句话说,M 次。这就是复杂性中有 M 的原因。

不幸的是,如果你想使用简单的堆,复杂度将是 O(M* log M)(或 M log N)。为什么?您无法快速更改堆中的值。因此,如果 dist[v] 突然减小,因为您找到了一条新的、更好的通往 v 的路径,您不能只在堆中更改它,因为您并不真正知道它的位置。您可以将 v 的副本放入堆中,但堆的大小将是 O(M)。即使您存储位置并巧妙地更新它,堆中可能有 O(N) 项,但您仍然必须在每次更改后更新堆,这需要 O(堆大小)。您最多可以将值更改为 O(M) 次,这会给您带来 O(M* log M (or N)) 复杂度

【讨论】:

以上是关于直接 Dijkstra 算法的时间复杂度的主要内容,如果未能解决你的问题,请参考以下文章

Dijkstra算法

最短路径-dijkstra算法

常用最短路优化算法及例题(附模板)——-SPFA和Dijkstra

Dijkstra最短路算法详解

Dijkstra 最短路径算法 秒懂详解

用小根堆实现dijkstra,求图的单源最短路径