|
1 | 1 | # 743. Network Delay Time
|
2 | 2 |
|
3 |
| -## Dijkstra's Algorithm Solution |
4 |
| -- Run-time: O(N^2) |
5 |
| -- Space: O(N) |
6 |
| -- N = Number of Nodes |
| 3 | +## Simple Dijkstra's Algorithm Solution |
| 4 | +- Run-time: O(V^2) |
| 5 | +- Space: O(V) |
| 6 | +- V = Number of Vertices |
7 | 7 |
|
8 | 8 | This version of Dijkstra is fairly straightforward.
|
9 | 9 | For each iteration from 1 to N.
|
@@ -45,5 +45,60 @@ class Solution:
|
45 | 45 |
|
46 | 46 | ## Dijkstra's Algorithm Solution with Heaps
|
47 | 47 |
|
| 48 | +- Run-time: O((V + E)logV) |
| 49 | +- Space: O(V) |
| 50 | +- V = Vertices |
| 51 | +- E = Edges |
| 52 | + |
| 53 | +As explained in question #1135, there is no adaptable heap implementation in Python 3.7. |
| 54 | +Instead, we will be using a lazy deletion method, similar to question #1135. |
| 55 | + |
| 56 | +The only difference between Prim vs. Dijkstra is adding/updating with the previous weights in the heap nodes. |
| 57 | +In Prim's we only care about the next weight. |
| 58 | +In Dijkstra, since we want the shortest paths, we need to reevaluate the weights by checking if we can produce an even smaller weight. |
| 59 | +Therefore, we need to compare between the shortest path to neighboring vertex vs. the current vertex's shortest path + current weight to neighboring vertex. |
| 60 | + |
48 | 61 | ```
|
| 62 | +from collections import defaultdict |
| 63 | +
|
| 64 | +class Solution: |
| 65 | + def networkDelayTime(self, times: List[List[int]], N: int, K: int) -> int: |
| 66 | +
|
| 67 | + def create_adj_list(): |
| 68 | + adj_list = defaultdict(list) |
| 69 | + for source, target, time in times: |
| 70 | + adj_list[source].append([target, time]) |
| 71 | + return adj_list |
| 72 | +
|
| 73 | + def add_target(target, time): |
| 74 | + new_node = [time, target, False] |
| 75 | + target_to_heap_node[target] = new_node |
| 76 | + heapq.heappush(min_heap, new_node) |
| 77 | +
|
| 78 | + adj_list = create_adj_list() |
| 79 | + start_node = [0, K, False] # time, source, remove |
| 80 | + target_to_heap_node = dict({K: start_node}) # key=target, val=time |
| 81 | + min_heap = list([start_node]) |
| 82 | + times = defaultdict(lambda: float('inf')) # key=source, val=time |
| 83 | + times[K] = 0 |
| 84 | + visited = set() |
| 85 | + while min_heap: |
| 86 | + time, source, remove = heapq.heappop(min_heap) |
| 87 | + if remove: # lazy delete |
| 88 | + continue |
| 89 | + visited.add(source) |
| 90 | + for next_target, t in adj_list[source]: |
| 91 | + if next_target in visited: |
| 92 | + continue |
| 93 | + next_time = t + time |
| 94 | + if next_target in target_to_heap_node: |
| 95 | + node = target_to_heap_node[next_target] |
| 96 | + if next_time < node[0]: |
| 97 | + node[2] = True # lazy delete |
| 98 | + add_target(next_target, next_time) |
| 99 | + else: |
| 100 | + add_target(next_target, next_time) |
| 101 | + target_to_heap_node.pop(source) |
| 102 | + times[source] = min(times[source], time) |
| 103 | + return max(times.values()) if len(times) == N else -1 |
49 | 104 | ```
|
0 commit comments