Jay's Cookbook
Menu
  • Tags
  • Categories
  • Projects
Computer Science
OS
Network
Data Structure
Algorithm
Language
Code Architecture
Python
Javascript
Typescript
Java
Backend
Backend Theory
TypeORM
Node.js
NestJS
FastAPI
Frontend
HTML/CSS
React
Next.js
Data Engineering
DE Theory
MySQL
MongoDB
Elastic
Redis
Kafka
Spark
Airflow
AI
Basic
Pytorch
NLP
Computer Vision
Data Analytics
Statistics
Pandas
Matplotlib
DevOps
Git
Docker
Kubernetes
AWS
[Data Structure] Graph (3): Union-Find

[Data Structure] Graph (3): Union-Find

Jay Kim
Jay Kim 10 Apr 2024
[Data Structure] Graph (1): 그래프 [Data Structure] Graph (4): Dijkstra, Kruskal, Kahn's Algorithm

Table of Contents

  • Union-Find
    • Key points of Union-Find
    • Use Case of Union-Find
  • Implementation of Union-Find
    • Quick Find
    • Quick Union
    • Union by Rank
    • Path Compression

Union-Find

  • The primary use of Union-Find is to address the connectivity between the components of a network.

Key points of Union-Find

  • find: function finds the root node of a given vertex.
  • union: function unions two vertices and makes their root nodes the same.

Use Case of Union-Find

  • Determine Connection
  • Detect Cycle

Implementation of Union-Find

Quick Find

  • self.root의 인덱스는 자기 자신, 값은 루트 노드를 가리킴
  • self.root의 값 자체가 인덱스 노드의 루트 노드이기 때문에 find는 O(1)
  • x와 y를 union하기 위해 x와 같은 루트 노드를 가지는 모든 노드의 루트 노드를 y의 루트 노드로 변경 -> 항상 O(N)
  • find는 O(1). union은 O(N)
# Quick Find
class UnionFind:
    def __init__(self, size):
        self.root = [i for i in range(size)]
    
    def find(self, x):
        return self.root[x]
    
    def union(self, x, y):
        if self.root[x] != self.root[y]:
            root_x = self.find(x)
            root_y = self.find(y)
            for i in range(len(self.root)):
                if self.root[i] == root_x:
                    self.root[i] = root_y
    
    def connected(self, x, y):
        if self.find(x) == self.find(y):
            return True
        return False

uf = UnionFind(9)

edges = [(0, 1), (0, 2), (1, 3), (4, 8), (5, 6), (5, 7)]

for edge in edges:
    uf.union(*edge)

print(uf.connected(0, 3))
print(uf.connected(1, 5))
print(uf.connected(7, 8))

Quick Union

  • self.parent의 인덱스는 자기 자신, 값은 부모 노드를 가리킴
  • self.parent의 배열에서 계속 부모 노드를 타고 올라가면서 루트 노드가 나올 때 까지 반복 -> find는 O(N)
  • x의 루트 노드의 부모 노드를 y의 루트 노드로 변경 -> union도 O(N)
  • find와 union 모두 O(N)이기 때문에 Quick Union이 시간 측면에서 성능이 낮아보이지만 union은 최악의 경우만 O(N)이지 Quick Find처럼 항상 O(N)은 아니다. 두 메소드 모두 정확히는 루트 노드를 찾는 과정만 필요로 하므로 정확히는 O(H)이다.
  • union, find -> O(H). (H는 해당 노드에서 루트 노드까지의 높이)
# Quick Union
class UnionFind:
    def __init__(self, size):
        self.parent = [i for i in range(size)]
    
    def find(self, x):
        while x != self.parent[x]:
            x = self.parent[x]
        return x
    
    def union(self, x, y):
        if self.parent[x] != self.parent[y]:
            root_x = self.find(x)
            root_y = self.find(y)
            self.parent[root_x] = root_y
    
    def connected(self, x, y):
        if self.find(x) == self.find(y):
            return True
        return False

Union by Rank

  • rank: 각 노드의 높이
  • union하고자 하는 두 트리의 루트 노드의 rank를 비교
  • rank가 높은 트리의 루트 노드가 union된 결과의 루트 노드가 되도록 하자 -> 최종 결과 트리의 높이가 유지될 수 있음
  • 두 트리의 높이가 같으면 임의로 하나를 기준 루트 노드로 잡는다 -> 하지만 rank가 1증가 할 수 밖에 없음
  • Quick-Union의 union 메서드를 개선한 방법
  • Quick Union에서 O(H)가 O(N)이 아닌 O(logN)으로 되도록 함
  • union된 결과가 일자로 길어지는 상황을 피할 수 있음

# Union by rank
class UnionFind:
    def __init__(self, size):
        self.parent = [i for i in range(size)]
        self.rank = [1] * size
    
    def find(self, x):
        while x != self.parent[x]:
            x = self.parent[x]
        return x
    
    def union(self, x, y):
        if self.parent[x] != self.parent[y]:
            root_x = self.find(x)
            root_y = self.find(y)
            if self.rank[root_x] > self.rank[root_y]:
                self.parent[root_y] = root_x
            elif self.rank[root_x] < self.rank[root_y]:
                self.parent[root_x] = root_y
            else:
                self.parent[root_x] = root_y
                self.rank[root_y] += 1
            
    
    def connected(self, x, y):
        if self.find(x) == self.find(y):
            return True
        return False

Path Compression

  • 기존 Quick Union에서 find는 루트 노드를 찾기 위해 parent를 계속 traverse함
  • If we search the root node of the same element again, we repeat the same operations. Is there any way to optimize this process?
  • After finding the root node, we can update the parent node of all traversed elements to their root node. When we search for the root node of the same element again, we only need to traverse two elements to find its root node, which is highly efficient. So, how could we efficiently update the parent nodes of all traversed elements to the root node? The answer is to use “recursion”. This optimization is called “path compression”, which optimizes the find function.
  • 대상 노드의 루트 노드를 찾는 과정에서 거쳐가는 모든 노드의 루트 노드를 찾아 저장한다

# Path compression
class UnionFind:
    def __init__(self, size):
        self.parent = [i for i in range(size)]
        self.rank = [1] * size
    
    # Path Compression
    def find(self, x):
        if x == self.parent[x]:
            return x
        self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    
    def union(self, x, y):
        if self.parent[x] != self.parent[y]:
            root_x = self.find(x)
            root_y = self.find(y)
            if self.rank[root_x] > self.rank[root_y]:
                self.parent[root_y] = root_x
            elif self.rank[root_x] < self.rank[root_y]:
                self.parent[root_x] = root_y
            else:
                self.parent[root_x] = root_y
                self.rank[root_y] += 1
            
    
    def connected(self, x, y):
        if self.find(x) == self.find(y):
            return True
        return False
[Data Structure] Graph (1): 그래프 [Data Structure] Graph (4): Dijkstra, Kruskal, Kahn's Algorithm

You may also like

See all Data-Structure
13 Apr 2024 [Data Structure] Graph (4): Dijkstra, Kruskal, Kahn's Algorithm

[Data Structure] Graph (4): Dijkstra, Kruskal, Kahn's Algorithm

03 Apr 2024 [Data Structure] Graph (1): 그래프

[Data Structure] Graph (1): 그래프

10 Mar 2024 [Data Structure] Binary tree (3): 힙(Heap)
CS
Data-Structure

[Data Structure] Binary tree (3): 힙(Heap)

Jay Kim

Jay Kim

Web development, data engineering for human for the Earth. I share posts, free resources and inspiration.

Rest
Lifestyle
Hobby
Hobby
Hobby
Hobby
2025 © Jay's Cookbook. Crafted & Designed by Artem Sheludko.