python實現圖(基於圖的不同儲存方式)的深度優先(DFS)和廣度(BFS)優先遍歷

python小明發表於2020-10-27

對於圖的遍歷操作,一般有兩種方法:深度優先遍歷和廣度優先遍歷。

這裡我們說的圖的遍歷是針對連通圖而言的,即圖中任意的兩節點都有一條或多條路徑能夠相同。

深度優先: 顧名思義,一條路走到黑,選取一個圖中的一個節點中作為起始結點,從起始結點開始,不聽的訪問該節點的下一個結點,直到訪問到該節點沒有下一個結點與其相連或者與其相連的結點都被訪問過,則回退至上一步,看其上一個結點是否還有沒被訪問的結點,如果有,則繼續按照深度優先訪問,沒有就繼續回退,直到確認圖中所有的結點都被訪問到,則遍歷結束。

由此,我們可以看出,在圖的深度優先搜尋過程中存在一個回溯過程,當訪問到的結點沒有與它相連的結點,或者與其相連的結點都被訪問過時,就需要回溯到上一個結點。由於棧存在先進後出的特點,所以我們可以用棧來實現這個過程。
如果訪問到該結點的鄰接點未被訪問,則將其第一個未被訪問的結點新增入棧,直到沒有或者所有鄰接點都被訪問過則進行出棧,沒出棧一個元素,都要判斷其是否還有未被訪問的鄰接點,直到棧中所有元素都出棧或者確認圖中所有結點都被訪問完,則搜尋過程結束。

!!!注意:
以下只給出圖的深度遍歷和廣度優先遍歷方法,至於圖的儲存請參考本人部落格:
圖的三種儲存方式(字典形式,鄰接矩陣形式,鄰接連結串列形式)

圖的鄰接表儲存,深度優先遍歷python實現:

def depth_order(start_node):
    '''
    基於圖的鄰接連結串列儲存表示,圖的深度優先遍歷
    :param start_node: 
    :return: 
    '''
    n = int(input("請輸入圖中結點的個數:"))
    # 例項化圖物件
    my_graph = graph()
    # 建立結點
    for i in range(0, n):
        data = input("請輸入結點:")
        # 例項化圖結點物件
        data = graph_node(data)
        # 將結點新增至圖中
        my_graph.add_node(data)
    # print("圖中所有頂點:")
    for nn in my_graph.graph:
        print(nn.data, end='\t')
    print(" ")
    # 建立圖的鄰接連結串列
    my_graph.create_graph_adjacencyList()
    # 列印圖的鄰接連結串列
    my_graph.print_graph_adjacencyList()


    # #遍歷圖的鄰接連結串列,進行深度遍歷
    #定義查詢棧
    node_stack = []
    #定義被訪問列表
    visited_list = []
    #使用臨時列表從圖中查詢起始結點
    temp_list = []
    for node in my_graph.graph:
        temp_list.append(node.data)
    #如果起始結點在圖中,加入被訪問列表中,加入查詢棧中,
    if my_graph.graph[temp_list.index(start_node)].data == start_node:
        #新增入被訪問列表
        visited_list.append(my_graph.graph[temp_list.index(start_node)].data)
        #加入查詢棧中
        node_stack.append(my_graph.graph[temp_list.index(start_node)])
        print(my_graph.graph[temp_list.index(start_node)].data)


    #若查詢棧不為空,迴圈查詢
    while node_stack != []:
        #以查詢棧的元素為起始結點,查詢它的連結串列,找出它的第一個鄰接點
        find_node = node_stack[-1]
        #迴圈查詢其鄰接連結串列
        current_node = find_node
        current_node = current_node.next
        #定義標誌位,標誌其鄰接連結串列中的所有鄰接點全部被訪問

        allvisited = 1
        while current_node != None:
            #如果該鄰接點未被訪問過,則說明該節點還有鄰接點未被訪問,將標誌位修改

            if current_node.data not in visited_list:
                allvisited = 0
                break
            current_node = current_node.next

        #如果該節點為None或者該節點的所有鄰接點都被訪問過,則出棧
        if find_node.next == None or allvisited == 1:
            node_stack.pop()
        #如果該節點不是None或者該節點的所有鄰接點沒有全被訪問,則繼續查詢其下一個未被訪問的鄰接點
        else:
            find_node1 = node_stack[-1]
            find_node1 = find_node1.next
            #如果找出該節點的鄰接點不為None,繼續查詢
            while find_node1 != None:
                #如果該節點的鄰接點不為None,並且該鄰接點未被訪問,則繼續查詢該鄰接點的下一個為被訪問的鄰接點
                if find_node1.data not in visited_list:
                    #若找到的鄰接點未被訪問,加入查詢棧中
                    node_stack.append(find_node1)
                    visited_list.append(find_node1.data)
                    print(find_node1.data)
                    break
                find_node1 = find_node1.next

if __name__ == '__main__':
    start_node = input("請輸入深度優先遍歷的起始結點:")
    depth_order(start_node)

廣度優先: 圖的廣度優先遍歷有些類似於樹的層次遍歷,從遍歷的起始點出發,遍歷與該節點相鄰的每一個結點,然後再從與該節點相鄰接的結點中繼續按照廣度優先原則訪問,直到圖中所有的結點都被訪問,遍歷過程結束。
由此看出,圖的廣度優先遍歷不需要回溯過程,類似於圖的層次遍歷,可以藉助佇列來實現圖的廣度優先遍歷。

圖的鄰接矩陣儲存,廣度優先遍歷python實現:

def breadth_order():
    '''
    基於圖的鄰接矩陣儲存,圖的廣度優先遍歷
    :return: 
    '''
    n = int(input("請輸入結點的個數:"))
    #例項化圖
    my_graph = graph(n)
    #向圖中新增結點
    my_graph1 = my_graph.add_node(n)
    print("圖中所有節點:",my_graph1)
    #構造圖的鄰接矩陣
    my_graph_matrix = my_graph.create_graph_matrix(my_graph1)
    # 列印鄰接矩陣
    print("圖的鄰接矩陣:")
    my_graph.print__graph_matrix(n)

    #構造被訪問結點陣列,若待訪問結點在此陣列中,則不需要入隊
    visited_node_list = []
    node_list = []
    print("圖的廣度優先遍歷(鄰接矩陣儲存形式):")
    for i in range(n):
        for j in range(n):
            if my_graph_matrix[i][j] == 1:
                node = my_graph1[i]

                if node not in visited_node_list:
                    visited_node_list.append(node)
                    node_list.append(node)
                    order_node = node_list.pop(0)

                    print(order_node,end='\t')
if __name__ == '__main__':
    breadth_order()

未完待續… …

相關文章