6

What is the time complexity of this particular implementation of Dijkstra's algorithm?

I know several answers to this question say O(E log V) when you use a min heap, and so does this article and this article. However, the article here says O(V+ElogE) and it has similar (but not exactly the same) logic as the code below.

Different implementations of the algorithm can change the time complexity. I'm trying to analyze the complexity of the implementation below, but the optimizations like checking visitedSet and ignoring repeated vertices in minHeap is making me doubt myself.

Here is the pseudo code:

// this part is O(V)
for each vertex in graph {
  distanceMap[vertex] = infinity
}

// initialize source vertex
minHeap.add(source vertex and 0 distance)
distanceMap[source] = 0
visitedSet.add(source vertex)

// loop through vertices: O(V)?
while (minHeap is not empty) {

  // Removing from heap is O(log n) but is n the V or E?
  vertex and distance = minHeap.removeMin
  if (distance > saved distance in distanceMap) continue while loop

  visitedSet.add(vertex)

  // looping through edges: O(E) ?
  for (each neighbor of vertex) {
    if (visitedSet contains neighbor) continue for loop

    totalDistance = distance + weight to neighbor
    if (totalDistance < saved distance in vertexMap) {

      // Adding to heap is O(log n) but is n the V or E?
      minHeap.add(neighbor and totalDistance)
      distanceMap[neighbor] = totalDistance
    }
  }
}

Notes:

  • Each vertex that is reachable from the source vertex will be visited at least once.
  • Each edge (neighbor) of each vertex is checked but ignored if in visitedSet
  • A neighbor is added to the heap only if it has a shorter distance that the currently known distance. (Unknown distances are assumed to have a default length of infinity.)

What is the actual time complexity of this implementation and why?

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • Just to be sure: nothing is to be changed to this pseudo code and you are not looking to optimise it, right? – trincot Dec 21 '21 at 09:09
  • As you mentioned in your code, minHeap stores the distances that are the weight of the edges, however, the number of these weights are at most the number of vertices. So, the while loop iterates at most O(v) times, and adding to or removing from heap will be O(log(v)). Moreover, I believe that looping over the neighbour of a vertex is also O(v) and not O(E) as there are at most v-1 neighbours for a particular vertex. – Mojtaba Valizadeh Dec 21 '21 at 09:39
  • 1
    there's no dijkstra algorithm that performs O(E + log V). Most of them are O(E * log V). I visited your reference [link](https://www.baeldung.com/cs/dijkstra-time-complexity) but it indicated the time complexity as O(E * log V). – Code Plus Dec 21 '21 at 14:31
  • @trincot, Yes, that is correct. – Suragch Dec 21 '21 at 23:10
  • @CodePlus, Thank you for catching my mistake. I've updated the question. – Suragch Dec 21 '21 at 23:12
  • *"Each vertex must be visited at least once"*: the algorithm will only visit vertices that can be reached from the start node. So must we assume the graph is connected? – trincot Dec 21 '21 at 23:14
  • @trincot, Good question. I hadn't considered that. I'll update the comment. – Suragch Dec 21 '21 at 23:17

1 Answers1

5
  1. Despite the test, this implementation of Dijkstra may put Ω(E) items in the priority queue. This will cost Ω(E log E) with every comparison-based priority queue.

  2. Why not E log V? Well, assuming a connected, simple, nontrivial graph, we have Θ(E log V) = Θ(E log E) since log (V−1) ≤ log E < log V² = 2 log V.

  3. The O(E + V log V)-time implementations of Dijkstra's algorithm depend on a(n amortized) constant-time DecreaseKey operation, avoiding multiple entries for an individual vertex. The implementation in this question will likely be faster in practice on sparse graphs, however.

David Eisenstat
  • 64,237
  • 7
  • 60
  • 120
  • **Question 1**: On your point 1, you use Ω(E log E). My understanding is that Ω is the lower bounds, O is upper bounds, and Θ is a tight or exact bounds. Does that mean I couldn't say O(E log E) because there isn't such an upper bounds? – Suragch Dec 22 '21 at 09:37
  • **Question 2**: Since I start the algorithm by visiting every vertex, wouldn't that change it to Ω(V + E log E)? Or does the V cancel out since it can be assumed that V < E. – Suragch Dec 22 '21 at 09:39
  • For other readers of this answer, here are some links I found useful while trying to understand it: [O vs Ω vs Θ](https://www.youtube.com/watch?v=0oDAlMwTrLo) and [DecreaseKey](https://www.baeldung.com/cs/min-heaps-decrease-key). – Suragch Dec 22 '21 at 09:43
  • 1
    @Suragch 1. if you used an inefficient queue, it could be Theta(E^2) or worse. 2. This is another reason to use Omega here, though I was specifically quoting the cost of the queue operations. Yes, the overall running time of the algorithm would technically have to include a V in general, but if V-1 ≤ E (e.g., connected graph) then we can fold the V into the E log E or E log V. – David Eisenstat Dec 22 '21 at 13:16
  • Assuming I do use an efficient queue (a min priority queue based on a heap with O(log n) time for enqueue and dequeue) and assuming a connected graph, can I say the complexity of the algorithm is O(E log E)? The reason I'm asking is that I'm trying to find a way to explain this algorithm using Big O notation rather than introducing the other notations. – Suragch Jan 03 '22 at 07:25
  • @Suragch yes, you can. – David Eisenstat Jan 03 '22 at 12:24