def flip_inside_O_by_UF(board: List[List[str]]) -> None: if not board: board = [] return uf = UnionFind(board) dummyIdx = uf.r * uf.c for i in range(uf.r): for j in range(uf.c): if board[i][j] == 'O': ijIdx = i * uf.c + j # connect all boundary nodes to the ONE dummy node if i in (0, uf.r - 1) or j in (0, uf.c - 1): uf.union(ijIdx, dummyIdx) # check its right neighbor is connected ? if j + 1 < uf.c and board[i][j + 1] == 'O': uf.union(ijIdx, ijIdx + 1) # check its down neighbor is connected ? if i + 1 < uf.r and board[i + 1][j] == 'O': uf.union(ijIdx, ijIdx + uf.c) for i in range(uf.r): for j in range(uf.c): if uf.find_root(i * uf.c + j) != uf.find_root(uf.dummyIdx): board[i][j] = 'X'
def num_similiar_groups_using_union_find(A: List[str]) -> int: def is_similiar(w1, w2): cnt = 0 for i1, i2 in zip(w1, w2): cnt += (i1 != i2) if cnt > 2: return False return cnt == 2 uf = UnionFind(A) strlen, wordlen = len(A[0]), len(A) if wordlen < strlen * strlen: for i, w1 in enumerate(A[:-1]): for w2 in A[i + 1:]: if is_similiar(w1, w2): uf.union(w1, w2) else: given_words = set(A) for w in given_words: for i in range(strlen): for j in range(i + 1, strlen): new = w[:i] + w[j] + w[i + 1:j] + w[i] + w[j + 1:] if new in given_words: uf.union(w, new) return len({uf.find_root(w) for w in uf.p})
def rm_stones_using_union_find(stones: List[List[int]]) -> int: if not stones: return 0 uf = UnionFind() for x, y in stones: for i, j in stones: if x == i or y == j: uf.union(10000 * x + y, 10000 * i + j) return len(uf.p) - len({uf.find_root(10000 * x + y) for x, y in stones})
def min_malware_spread_using_union_find(graph: List[List[int]], initial: List[int]) -> int: N = len(graph) uf = UnionFind(N) for i in range(N): for j in range(i): if graph[i][j]: uf.union(i, j) a_roots_count = collections.Counter([uf.find_root(i) for i in uf.p]) v_roots_count = collections.Counter([uf.find_root(v) for v in initial]) cnt, idx = 0, min(initial) for vid in initial: v_root = uf.find_root(vid) if v_roots_count[v_root] == 1: if a_roots_count[v_root] > cnt: cnt, idx = a_roots_count[v_root], vid elif a_roots_count[v_root] == cnt: idx = min(idx, vid) return idx
def min_malware_spread_II_using_unionfind(graph: List[List[int]], initial: List[int]) -> int: N = len(graph) clean = set(list(range(N))) - set(initial) uf = UnionFind(N) for i in clean: for j in clean: if graph[i][j]: uf.union(i, j) # global view from virus # one virus can impact which groups: id by root of a group impact_clean_nodes = collections.defaultdict(set) for v in initial: for i in clean: if graph[v][i]: impact_clean_nodes[v].add(uf.find_root(i)) # global view from clean node # one clean node can be impacted by how many virus impact_clean_counts = collections.Counter() for v in initial: for i in impact_clean_nodes[v]: impact_clean_counts[i] += 1 max_impact_num = -1 ans = min(initial) # loop with each virus to see # how many groups it can independently impact for v, some_clean_nodes in impact_clean_nodes.items(): cnt = 0 for i in some_clean_nodes: if impact_clean_counts[i] == 1: cnt += uf.sz[i] if cnt > max_impact_num or (cnt == max_impact_num and v < ans): max_impact_num = cnt ans = v return ans
def regions_by_slashes_using_union_find(grid: List[str]) -> int: if not grid: return 0 uf = UnionFind(grid) N = len(grid) for i, row in enumerate(grid): for j, slash in enumerate(row): ijIdx = 4 * (i * N + j) # 1 # 0 3 # 2 if slash == '/': uf.union(ijIdx + 0, ijIdx + 1) uf.union(ijIdx + 2, ijIdx + 3) elif slash == '\\': uf.union(ijIdx + 0, ijIdx + 2) uf.union(ijIdx + 1, ijIdx + 3) else: uf.union(ijIdx, ijIdx + 1) uf.union(ijIdx, ijIdx + 2) uf.union(ijIdx, ijIdx + 3) # union down 1/4 cell except last row N - 1 if i < N - 1: uf.union(ijIdx + 2, ijIdx + 4 * N + 1) # union up 1/4 cell except first row 0 if i >= 1: uf.union(ijIdx + 1, ijIdx - 4 * N + 2) # union right 1/4 cell except first column N - 1 if j < N - 1: uf.union(ijIdx + 3, ijIdx + 4) # union left 1/4 cell except last column 0 if j >= 1: uf.union(ijIdx + 0, ijIdx - 1) return sum(uf.find_root(i) == i for i in range(4 * N * N))
def calc_equation_by_union_find(equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]: uf = UnionFind(equations) for i, e in enumerate(equations): numer, deno = e factor = values[i] uf.union(numer, deno, factor) ans = [] for x, y in queries: xRoot = uf.find_root(x) yRoot = uf.find_root(y) # new variable not exist in equations, result must -1.0 if None in (xRoot, yRoot): ans.append(-1.0) else: if xRoot != yRoot: ans.append(-1.0) else: ans.append(uf.factor[x] / uf.factor[y]) return ans
def rm_stones_using_union_find_simple_view(stones): uf = UnionFind() for x, y in stones: uf.union(x, 10000 + y) return len(stones) - len({uf.find_root(i) for i, _ in stones})