You are given an integer n. There is an undirected graph with n nodes, numbered from 0 to n - 1. You are given a 2D integer array edges where edges[i] = [ai, bi] denotes that there exists an undirected edge connecting nodes ai and bi.
Return the number of pairs of different nodes that are unreachable from each other.
Input: n = 3, edges = [[0,1],[0,2],[1,2]]
Output: 0
Explanation: There are no pairs of nodes that are unreachable from each other. Therefore, we return 0.
Input: n = 7, edges = [[0,2],[0,5],[2,4],[1,6],[5,4]]
Output: 14
Explanation: There are 14 pairs of nodes that are unreachable from each other:
[[0,1],[0,3],[0,6],[1,2],[1,3],[1,4],[1,5],[2,3],[2,6],[3,4],[3,5],[3,6],[4,6],[5,6]].
Therefore, we return 14.
如果2個node没有法通往對方,代表他們在不同的tree上,如果我們能找出總共有幾棵樹、每棵樹上面有幾個node就能藉此計算出unreachable 的node pair。這時,我們可以用union find來將所有的node分類進不同的tree中。
維基百科給出的定義如下
In computer science, a disjoint-set data structure, also called a union–find data structure or merge–find set, is a data structure that stores a collection of disjoint (non-overlapping) sets. Equivalently, it stores a partition of a set into disjoint subsets. It provides operations for adding new sets, merging sets (replacing them by their union), and finding a representative member of a set. The last operation makes it possible to find out efficiently if any two elements are in the same or different sets.
Union find提供了3個method: add
find
merge
add
是將一個node加入到一個set(tree)中find
會從一個node開始,往上找parent,直到找出一個set中的representative member或是tree的rootmerge
則是試圖將兩個disjoint的set合併union find 還有更進階的版本是可以依照每個node的rank去找root,但由於本題只需要知道有幾個disjoint set所以不需要實作這個部分。
首先我們建立一個Array來maintain每個node所在的樹的root,一開始假設所有的node都是disjoint的樹,所以大家的root都是自己
以example 1為例,現在parent
中的值如下:
node | 0 | 1 | 2 |
---|---|---|---|
parent | 0 | 1 | 2 |
接下來我們要實作union find的3個method。首先,add
是將node加入倒tree中,由於我們在這裡沒有要實際建立tree,所以不需要實做這個function,而find
的function如下:
這個function會接收前面的parent array與要找parent的node作為input,並return該node所在的tree root的編號。其中path compression的部分,則是當這個node不是這顆tree中的root時,要一 層一層的往上找到root,以確保我們在parent array中所存的node都是該棵樹的root id,同時也可以將路徑上經過的parent node都更新其parent直到最新的root。
接著時做union
function:
這個function會接收parent array與2個node作為input,並利用前面的find
function 檢查這兩個node是否有同一個root,若是,則將兩棵樹併起來,實際的操作為:將其中一棵樹的root(parentV
)的parent更新為另一棵樹的root(parentU
)。
這邊就可以看到前面path compression的作用體現出來了,如果在find的時候沒有做path compression,有可能明明兩個node是在同一棵樹底下,但拿到的卻是路徑中的parent而非root,以至於第5行的判斷式不通過,而沒有更新。
最後,利用merge
function對於每一個edge的node pair做一次merge,將相連的node倆倆合併,最後再檢查總共有幾個root就可以知道有幾棵disjoint tree
注意在做完所有的merge之後,要再對每一個node做一次find,利用function中的path compression來確保parent array中所有的值都是該棵樹中的root。
最後loop through parent array統計出每棵樹有幾個node,存於size
這個變數中,對於每一棵樹,unreachable node pair的數量就是自己的node數 * 不在該棵樹上的node數,最後由於會重複計算,所以結果再除以2即可。