並查集原理及Python實現,朋友圈個數問題
阿新 • • 發佈:2019-01-27
背景問題:給定一些好友的關係,求這些好友關係中,存在多少個朋友圈?
例如給定好友關係為:[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))
執行結果如下:
參考