Code Templates
Union Find
Use Cases for Trie Data Structures
Use Case | Description | Example |
---|---|---|
Connected Components | Determine if two nodes are in the same connected component in a graph. | In a social network, check if two users are in the same group. |
Network Connectivity | Monitor the connectivity of a network as connections are added or removed. | In a computer network, determine if two devices can communicate after a new connection is made. |
Kruskal's Minimum Spanning Tree | Use Union-Find to detect cycles while constructing a minimum spanning tree. | In a graph of cities, find the minimum cost to connect all cities without any cycles. |
Code Template
Initializing variables
self.parent = [i for i in range(size)]:
Creates aparent
array where each element points to itself, indicating that each element is its own set initially.self.rank = [1] * size:
Initializes arank
array, where each element starts with a rank of 1, used to optimize union operations by keeping track of tree depth.
Find function
- This method is designed to find the root or representative of the set containing element
p
. if self.parent[p] != p:
This line checks if the element p is its own parent. If not, it meansp
is not the root of its set.self.parent[p] = self.find(self.parent[p]):
Ifp
is not the root, this line recursively calls find on p's parent. The result is then assigned back toself.parent[p]
, effectively flattening the structure (path compression). This means that all nodes in the path from p to its root will now point directly to the root, speeding up future queries.
Union function
rootP = self.find(p)
androotQ = self.find(q):
Uses the find method to determine the roots of the sets containingp
andq
.- The
root
with the higherrank
becomes the parent, maintaining a balanced tree structure. If ranks are equal, one root becomes the parent, and itsrank
is incremented.
Check connection
- Compares the roots of
p
andq
. If the roots are the same, it meansp
andq
are connected.
class UnionFind:def __init__(self, size):self.parent = [i for i in range(size)]self.rank = [1] * size
Common mistakes
- Wrong Path Compression Implementation
# Wrong: no path compressiondef find(self, p):while self.parent[p] != p: # Just traverses upp = self.parent[p]return p# Wrong: incorrect recursiondef find(self, p):if self.parent[p] != p:return self.find(self.parent[p]) # Doesn't compress# Correct:def find(self, p):if self.parent[p] != p:self.parent[p] = self.find(self.parent[p])return self.parent[p]
- Wrong Union by Rank Implementation
# Wrong: not using ranksdef union(self, p, q):rootP = self.find(p)rootQ = self.find(q)self.parent[rootQ] = rootP # Always merges to rootP# Wrong: incorrect rank updatedef union(self, p, q):if self.rank[rootP] == self.rank[rootQ]:self.rank[rootP] += 1self.rank[rootQ] += 1 # Should only increment one# Correct:if self.rank[rootP] == self.rank[rootQ]:self.parent[rootQ] = rootPself.rank[rootP] += 1
- Wrong Connected Check
# Wrong: not using finddef connected(self, p, q):return self.parent[p] == self.parent[q] # Should use find()# Wrong: redundant findsdef connected(self, p, q):rootP = self.find(p)rootQ = self.find(q)return self.find(rootP) == self.find(rootQ) # Unnecessary finds# Correct:def connected(self, p, q):return self.find(p) == self.find(q)
Issues and consideration
- Thread Safety: If the class is intended for concurrent use, consider implementing locking mechanisms to prevent race conditions.
Related Problem
class UnionFind:def __init__(self, size):self.parent = [i for i in range(size)]self.rank = [1] * sizedef find(self, p):if self.parent[p] != p:self.parent[p] = self.find(self.parent[p])return self.parent[p]def union(self, p, q):rootP = self.find(p)rootQ = self.find(q)if rootP != rootQ:if self.rank[rootP] > self.rank[rootQ]:self.parent[rootQ] = rootPelif self.rank[rootP] < self.rank[rootQ]:self.parent[rootP] = rootQelse:self.parent[rootQ] = rootPself.rank[rootP] += 1def connected(self, p, q):return self.find(p) == self.find(q)class Solution:def numIslands(self, grid: List[List[str]]) -> int:if not grid:return 0rows = len(grid)cols = len(grid[0])# Initialize UnionFind with size rows*colsuf = UnionFind(rows * cols)# Count initial land cells and connect adjacent landscount = 0for i in range(rows):for j in range(cols):if grid[i][j] == "1":count += 1# Check right and down neighborsfor di, dj in [(0, 1), (1, 0)]:ni, nj = i + di, j + djif (0 <= ni < rows and0 <= nj < cols andgrid[ni][nj] == "1"):if not uf.connected(i*cols + j, ni*cols + nj):uf.union(i*cols + j, ni*cols + nj)count -= 1return count
class UnionFind:def __init__(self, size):self.parent = [i for i in range(size)]self.rank = [1] * sizedef find(self, p):if self.parent[p] != p:self.parent[p] = self.find(self.parent[p])return self.parent[p]def union(self, p, q):rootP = self.find(p)rootQ = self.find(q)if rootP != rootQ:if self.rank[rootP] > self.rank[rootQ]:self.parent[rootQ] = rootPelif self.rank[rootP] < self.rank[rootQ]:self.parent[rootP] = rootQelse:self.parent[rootQ] = rootPself.rank[rootP] += 1def connected(self, p, q):return self.find(p) == self.find(q)class Solution:def findCircleNum(self, isConnected: List[List[int]]) -> int:if not isConnected:return 0n = len(isConnected)uf = UnionFind(n)# Connect cities based on the adjacency matrixfor i in range(n):for j in range(i + 1, n):if isConnected[i][j] == 1:uf.union(i, j)# Count number of distinct provincesprovinces = set()for i in range(n):provinces.add(uf.find(i))return len(provinces)