python 無向圖最短路徑之Dijkstra算法

 無向圖:

在數據結構中的無向圖通常使用鄰接矩陣表示

無向圖的鄰接矩陣是對稱矩陣,有向圖的鄰接矩陣不是對稱矩陣。

共有5個頂點(nodes),7條邊(vertices)

其鄰接矩陣爲:num_node*num_node,矩陣中的數值表示兩個相連接的節點的邊的權值

節點 A B C D E
A 0 6 inf 1 inf
B 6 0 5 2 2
C inf 5 0 inf 5
D 1 2 inf 0 1
E inf 2 5 1 0

在無向圖中尋找最短路徑通常使用的是Dijkstra算法和Floyd算法。

Dijkstra算法:給定某個特定的起始頂點,找到從起始頂點到圖中所有頂點的距離最小值(最短路徑),其時間複雜度爲O(num_node**2)。

Floyd算法:找到從圖中的所有頂點到達任意頂點的最短路徑,其時間複雜度爲O(num_node**3)。

一、Dijkstra算法

 

Dijkstra算法:其時間複雜度爲O(n**2),其中n表示圖中的節點個數

Dijkstra算法是基於兩個列表的,分別是visited和unvisited

當前無向圖中具有5個頂點。

假設是要找到從A到達所有節點最短的距離

第一次循環

在distance列表中找到所有unvisited列表中的節點所具有的最小distance值,發現是A距離最小(實際上就是初始節點/start node)

current_node=A

找到A在無向圖中的鄰居節點,發現是B,D。正好B,D都在unvisited列表中

distance[B]=min(distance(B),distance(A)+vertice(A,B))=min(float('inf'),0+6) =6  故更新distance列表中B的值

distance[D]=min(distance(D),distance(A)+vertice(A,D))=min(float('inf'),0+2)=1  故更新distance列表中D的值

將節點A從unvisted列表中刪除,放到visited列表中

此時

visited=[A]

unvisited=[B,C,D,E]

distance[A,B,C,D,E]=[0,6,float(''inf),1,float(''inf)]

第二次循環

在distance列表中找到所有unvisited列表中的節點所具有的最小distance值,此時unvisted中是BCDE,則發現是D距離=1最小

current_node=D

找到D在無向圖中的鄰居節點,發現是A,B,E。A不在unvisited列表中,B,E在unvisited列表中。故不管A,只判斷B,E

distance[B]=min(distance(B),distance(D)+vertice(D,B))=min(6,1+2) =3  故更新distance列表中B的值

distance[E]=min(distance(E),distance(D)+vertice(D,E))=min(float('inf'),1+1) =2  故更新distance列表中E的值

將節點D從unvisted列表中刪除,放到visited列表中

此時

visited=[A,D]

unvisited=[B,C,E]

distance[A,B,C,D,E]=[0,3,float(''inf),1,2]

第三次循環

在distance列表中找到所有unvisited列表中的節點所具有的最小distance值,此時unvisted中是BCE,則發現是E距離=2最小

current_node=E

找到E在無向圖中的鄰居節點,發現是B,C,D。D不在unvisited列表中,B,C在unvisited列表中。故不管D,只判斷B,C

distance[B]=min(distance(B),distance(E)+vertice(E,B))=min(3,2+2) =3  故不更新distance列表中B的值

distance[C]=min(distance(C),distance(E)+vertice(E,C))=min(float(''inf),2+5) =7  故更新distance列表中C的值

將節點E從unvisted列表中刪除,放到visited列表中

此時

visited=[A,D,E]

unvisited=[B,C]

distance[A,B,C,D,E]=[0,3,7,1,2]

第四次循環

在distance列表中找到所有unvisited列表中的節點所具有的最小distance值,此時unvisted中是BC,則發現是B距離=4最小

current_node=B

找到E在無向圖中的鄰居節點,發現是A,C,D。A,D不在unvisited列表中,C在unvisited列表中。故不管A,D,只判斷C

distance[C]=min(distance(C),distance(B)+vertice(B,C))=min(7,4+5) =7  故不更新 distance列表中C的值

將節點B從unvisted列表中刪除,放到visited列表中

此時

visited=[A,D,E,B]

unvisited=[C]

distance[A,B,C,D,E]=[0,3,7,1,2]

第五次循環

在distance列表中找到所有unvisited列表中的節點所具有的最小distance值,此時unvisted中是C,則發現是C距離=7最小

current_node=C

找到E在無向圖中的鄰居節點,發現是B,E。B,E不在unvisited列表中,故不對任何節點的距離進行判斷

將節點C從unvisted列表中刪除,放到visited列表中

此時

 

visited=[A,D,E,B,C]

unvisited=[]

distance[A,B,C,D,E]=[0,3,7,1,2]

所輸出的distance列表表示的就是:從起始節點A出發,到達無向圖中每個節點的最短路徑長度。

'''
給你n個點,m條無向邊,每條邊都有長度d和花費p,給你起點s終點t,
要求輸出起點到終點的最短距離及其花費,如果最短距離有多條路線,則輸出花費最少的。

輸入描述:
輸入n,m,點的編號是1~n,然後是m行,每行4個數 a,b,d,p,表示a和b之間有一條邊,且其長度爲d,花費爲p。
最後一行是兩個數 s,t;起點s,終點t。n和m爲0時輸入結束。
(1<n<=1000, 0<m<100000, s != t)

輸出描述:
輸出 一行有兩個數, 最短距離及其花費。

輸入
3 2
1 2 5 6
2 3 4 5
1 3
0 0
輸出
9 11

您的代碼已保存
運行超時:您的程序未能在規定時間內運行結束,請檢查是否循環有錯或算法複雜度過大。
case通過率爲90.91%

感謝
https://www.bilibili.com/video/av38254646  講的真的非常清楚

'''

if __name__=='__main__':
    line1=list(map(int,input().split()))
    num_node=line1[0]
    num_vertice=line1[1]

    no_direct_graph=[]

    for i in range(num_vertice):
        no_direct_graph.append(list(map(int,input().split())))

    start_end=list(map(int,input().split()))

    dict_graph={}
    for k in range(len(no_direct_graph)):#對於無向圖中的每一條邊
        for j in range(num_node):
            if no_direct_graph[k][0]==j+1:
                if j+1 not in dict_graph:
                    dict_graph[j+1]=[no_direct_graph[k]]
                else:
                    dict_graph[j + 1].append(no_direct_graph[k])
            elif no_direct_graph[k][1]==j+1:
                if j + 1 not in dict_graph:
                    dict_graph[j + 1] = [[no_direct_graph[k][1],no_direct_graph[k][0],no_direct_graph[k][2],no_direct_graph[k][3]]]
                else:
                    dict_graph[j + 1].append([no_direct_graph[k][1],no_direct_graph[k][0],no_direct_graph[k][2],no_direct_graph[k][3]])
    # print(dict_graph)   以字典的形式構造無向圖

    visited=[]
    unvisited=[_+1 for _ in range(num_node)]

    distance=[float('inf') for _ in range(num_node)]

    money=[0 for _ in range(num_node)]

    temp=start_end[0]
    # money[temp - 1] = 0
    distance[temp-1]=0
    for j in range(num_node):
        temp_neighbor = dict_graph[temp]
        for route in temp_neighbor:
            # print('route',route)
            if route[1] not in visited:  # 如果當前節點的鄰居節點沒有被訪問過
                if distance[temp - 1] + route[2]<distance[route[1] - 1]:
                    distance[route[1] - 1]=distance[temp - 1] + route[2]
                    # print(route[-1])
                    money[route[1] - 1]=money[temp-1]+route[-1]
                elif distance[temp - 1] + route[2]==distance[route[1] - 1]:#如果距離相等,取花費少的路徑
                    if money[temp-1]+route[-1]<money[route[1] - 1]:
                        distance[route[1] - 1]=distance[temp - 1] + route[2]
                        money[route[1] - 1] = money[temp - 1] + route[-1]
        # 找到money數組中除了當前temp的money值之外剩下的所有元素中最小money數量的位置,作爲下一個temp位置
        distance_compare = distance.copy()
        distance_compare[temp - 1] = float("inf")

        visited.append(temp)
        if temp in unvisited:
            unvisited.remove(temp)

        min_value = float("inf")
        min_index = 0

        for k in range(num_node):
            if k+1 in unvisited:#如果當前的節點並沒有被訪問過
                if distance_compare[k] < min_value:
                    min_value = distance_compare[k]
                    min_index = k
        temp = min_index + 1

    print(distance[start_end[-1]-1],money[start_end[-1]-1])

    # min_distance=min(distance)
    # min_money=[]
    # for i,q in enumerate(distance):
    #     if q==min_distance:
    #         min_money.append(money[i])
    # print(min_distance,min(min_money))

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章