今天要來分享的是利用了Greedy策略去解決在資料結構中的Graph上某個節點到其餘的節點的最短路徑問題,其中的經典演算法:Dijkstra's Algorithm。
這個演算法需要對Graph和Heap這兩個資料結構有一些認識才比較好理解:)
那我們就開始吧。
敘述:如果有五個城市分別命名為A~E,如圖所示,我們想從城市C以最短的路徑到達其他城市。請輸出到達個城市的最短距離。
分析:我們打算以greedy的策略去解決這個問題,「永遠都先挑從該節點到達另一個節點的最短累積
距離」,其餘的則儲存起來。要以程式碼實作出上述的需求,有一個資料結構非常符合,那就是minimum heap。
累積
距離」起點C共有3條路徑可以走,他們一開始都會被存放到heap中,其中C到E擁有最短的距離所以會被pop出來,我們確信這就是C到E的最短距離所以把他紀錄下來。 接著再將E可以到達別的節點的距離「加上」C到達E的最短距離存放到heap中,因為我們的目標是C到個點的最短距離,所以需要累加。
另外,如果認為C到E有其他更短的距離,所以不應該直接確信當下從heap pop出來的C到E的距離為最短距離就會產生矛盾。因為C到B的距離以及C到D的距離都已經比C到E的距離長了,不可能再走其他節點的路徑到達E會有比C到E更短的距離了。所以這個greedy策略是可行的。同時這也帶出了這個演算法的限制,「Dijkstra's Algorithm 不可以有negative weight」。
import heapq
def shortestPath(edges, n, src):
adj = {}
for i in range(1, n + 1):
adj[i] = []
# s = src, d = dst, w = weight
for s, d, w in edges:
adj[s].append([d, w])
shortest = {}
minHeap = [[0, src]]
while minHeap:
w1, n1 = heapq.heappop(minHeap)
if n1 in shortest:
continue
shortest[n1] = w1
for n2, w2 in adj[n1]:
if n2 not in shortest:
heapq.heappush(minHeap, [w1 + w2, n2])
return shortest
為什麼要有:
if n1 in shortest:
continue
ans:
因為我們是以greedy的策略的解題,所以先被pop出來的節點和距離就是起點到達該節點的最短距離了。再pop出來一樣的節點的距離就應該忽略。
為什麼要有:
if n2 not in shortest:
heapq.heappush(minHeap, [w1 + w2, n2])
ans:
如果某個節點已經被記錄過起點到該節點的最短距離了,還是有可能因為有其他節點可以到達這個節點而再次出現,為了加速運算速度:我們的終止條件是heap變成空,所以需要設立條件去filter。
分析:worst case是所有的節點間連向除了自己以外所有的節點:這樣會有E條路徑被push到heap中。不管是push或是pop,heap的運算都是logn,所以複雜度為O(ElogE)。
# preprocess with graph
# ...
#initiate a heap
h = []
heapq.heappush(h, (0, src))
while h:
dis, node = heapq.heappop(h)
# ...
敘述:
Example:
Input: times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2
Output: 2
分析:
class Solution:
def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int:
cost = 0
adjList = defaultdict(list)
for src, dst, t in times:
adjList[src].append((dst, t))
h = [(0, k)]
visit = set()
while h:
curTime, node = h[0][0], h[0][1]
if node in visit:
heapq.heappop(h)
continue
cost = curTime
while h and h[0][0] == curTime:
t, nextSrc = heapq.heappop(h)
visit.add(nextSrc)
for dst, time in adjList[nextSrc]:
if dst not in visit:
heapq.heappush(h, (t + time, dst))
return cost if len(visit) == n else -1
謝謝大家觀看,明天中秋節繼續更新(太血汗啦 \(´◓Д◔`)/)