1. 程式人生 > >並查集原理及Python實現,朋友圈個數問題

並查集原理及Python實現,朋友圈個數問題

背景問題:給定一些好友的關係,求這些好友關係中,存在多少個朋友圈?

例如給定好友關係為:[0,1], [0, 4], [1, 2], [1, 3], [5, 6], [6, 7], [7, 5], [8, 9]。在這些朋友關係中,存在3個朋友圈,分別是

【0,1,2,3,4】,【5,6,7】,【8,9】

如下圖所示:

這個問題,抽象一下,就是:求一個圖的連通子圖的個數,即連通度是多少。

第一種方法,採用DFS遍歷這個圖,遍歷過程中,可以求出連通度,但是DFS對於大型圖,效率緩慢。

第二種方法,採用並查集。並查集可以說是一種演算法,或者資料結構。

並查集的主要思想是,對每一個連通的子圖,選出一個節點,作為代表。

“代表”的個數,就是連通度的大小。

步驟如下:

1. 初始化每個節點的代表為其本身(後面,把代表叫做“父節點”)。

2.針對給定的好友關係[0,1], [0, 4], [1, 2], [1, 3], [5, 6], [6, 7], [7, 5], [8, 9],更新父節點。例如給出(1,2)那麼,更新2的父節點為1。

3.重新更新所有節點的父節點,針對每個節點,找到其祖宗節點,即根節點。

對應的步驟如下:上面的是節點本身,下面的是節點對應的父節點或根節點。

這樣,就將節點分成了3類,每個類用一個節點作為代表。

Python程式碼如下:

def union_find(nodes, edges):
    father = [0]*len(nodes)     # 記錄父節點
    for node in nodes:  # 初始化為本身
        father[node] = node

    for edge in edges:  # 標記父節點
        head = edge[0]
        tail = edge[1]
        father[tail] = head

    for node in nodes:
        while True:         # 迴圈,直到找到根節點
            father_of_node = father[node]
            if father_of_node != father[father_of_node]:
                father[node] = father[father_of_node]
            else:           # 如果該節點的父節點與其爺爺節點相同,
                break       # 則說明找到了根節點

    L = {}
    for i, f in enumerate(father):
        L[f] = []
    for i, f in enumerate(father):
        L[f].append(i)

    return L


if __name__ == '__main__':
    nodes = list(range(0, 10))
    test_edges = [[0, 1], [0, 4], [1, 2], [1, 3], [5, 6], [6, 7], [7, 5], [8, 9]]

    L = union_find(nodes, test_edges)
    print(L)
    print('num of pyq:', len(L))

執行結果如下:

參考