๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
โญ Personal_Study/Algorithm

[์•Œ๊ณ ๋ฆฌ์ฆ˜] Union-Find

by ํฌ์ŠคํŠธ์‰์ดํฌ 2022. 9. 28.

์œ ๋‹ˆ์˜จํŒŒ์ธ๋“œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋ž€

โœ” ์„œ๋กœ์†Œ ์ง‘ํ•ฉ(Disjoint Set)
โœ” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋…ธ๋“œ๊ฐ€ ์กด์žฌํ•  ๋•Œ , ์ž„์˜์˜ ๋‘ ๋…ธ๋“œ๊ฐ€ ๊ฐ™์€ ๊ทธ๋ž˜ํ”„์— ์†ํ•ด์žˆ๋Š”์ง€ ํŒ๋ณ„

Union & Find

  • Find
    ๋…ธ๋“œ x๊ฐ€ ํฌํ•จ๋œ ์ง‘ํ•ฉ์„ ์ฐพ๋Š” ์—ฐ์‚ฐ
def  find_parent(parent, current):
     if  parent[current] != current:
         return  find_parent(parent, parent[current])
     return  current
  • Union
    ๋…ธ๋“œ x๊ฐ€ ํฌํ•จ๋œ ์ง‘ํ•ฉ๊ณผ ๋…ธ๋“œ y๊ฐ€ ํฌํ•จ๋œ ๋‘ ์ง‘ํ•ฉ์„ ํ†ตํ•ฉํ•˜๋Š” ์—ฐ์‚ฐ
def  union_parent(parent, x, y):
     x = find_parent(parent, x)
     y = find_parent(parent, y)
     # ๋‹จ์ˆœํ•œ ํ˜•ํƒœ์—์„œ๋Š” ์ž‘์€ ์ชฝ์œผ๋กœ ํ•ฉ์นœ๋‹ค.
     if  x < y:
         parent[y] = x
     else:
         parent[x] = y

์ฝ”๋“œ ์˜ˆ์‹œ

# ๋…ธ๋“œ์™€ ๊ฐ„์„ (union ์—ฐ์‚ฐ) ๊ฐœ์ˆ˜
node, edge = map(int, input().split())

# 0์œผ๋กœ ๋ถ€๋ชจ ๋ฐฐ์—ด ์ดˆ๊ธฐํ™”
parent = [0  for  _  in  range(node + 1)]

# ๋ถ€๋ชจ๋ฅผ ์ž๊ธฐ ์ž์‹ ์œผ๋กœ ์ดˆ๊ธฐํ™”
for  i  in  range(1, node + 1):
parent[i] = i


# union ์—ฐ์‚ฐ
for  i  in  range(edge):
a, b = map(int, input().split())
union_parent(parent, a, b)


# ๊ฐ ์›์†Œ๊ฐ€ ์†ํ•ด์žˆ๋Š” ์ง‘ํ•ฉ ์ถœ๋ ฅ
for  i  in  range(1, node + 1):
print(find_parent(parent, i), end=' ')
print()


# ๋ถ€๋ชจ ํ…Œ์ด๋ธ” ์ถœ๋ ฅ
for  i  in  range(1, node + 1):
print(parent[i], end=' ')

์ตœ์ ํ™”

๊ฒฝ๋กœ ์••์ถ• (Path compression)

# ๊ฒฝ๋กœ ์••์ถ• -> ๋ถ€๋ชจ๊ฐ’ ์žฌ๊ท€์ ์œผ๋กœ ๊ฐฑ์‹ 
def  find_parent_compression(parent, node):
    if  parent[node] != node:
        parent[node] = find_parent_compression(parent, parent[node])
    return  parent[node]

Rank ์ด์šฉ (Union by Rank)

# rank: ๊ฐ ๋…ธ๋“œ์˜ rank๋ฅผ ์ €์žฅํ•  ๋ฆฌ์ŠคํŠธ
rank = [0 for _ in range(N + 1)]

# def union_parent_rank(x, y):
    x = find_parent(x)
    y = find_parent(y)
    if x == y:
        return
    # ๋žญํฌ๊ฐ€ ๋‚ฎ์€ ์ง‘ํ•ฉ์„ ๋†’์€ ์ง‘ํ•ฉ์œผ๋กœ ๋ณ‘ํ•ฉํ•ด์ค€๋‹ค
    if rank[x] > rank[y]:
        parent[y] = x
    else:
        parent[x] = y
        # ๋žญํฌ๊ฐ€ ๋™์ผํ•  ๊ฒฝ์šฐ
        if rank[x] == rank[y]:
            rank[y] += 1

์ฝ”๋“œ ์˜ˆ์‹œ

def find_parent(x):
    if parent[x] != x:
        parent[x] = find_parent(parent[x])
    return parent[x]


def union_parent(x, y):
    x = find_parent(x)
    y = find_parent(y)
    if x == y:
        return
    if rank[x] > rank[y]:
        parent[y] = x
    else:
        parent[x] = y
        if rank[x] == rank[y]:
            rank[y] += 1


# ๋…ธ๋“œ์™€ ๊ฐ„์„ (union ์—ฐ์‚ฐ) ๊ฐœ์ˆ˜ 
node, edge =  map(int,  input().split())  
# 0์œผ๋กœ ๋ถ€๋ชจ ๋ฐฐ์—ด ์ดˆ๊ธฐํ™” 
parent =  [0  for _ in  range(node +  1)]
# 1๋กœ ๋žญํฌ ์ดˆ๊ธฐํ™”
rank = [1 for _ in range(node + 1)]

# ๋ถ€๋ชจ๋ฅผ ์ž๊ธฐ ์ž์‹ ์œผ๋กœ ์ดˆ๊ธฐํ™”
for  i  in  range(1, node + 1):
    parent[i] = i

# union ์—ฐ์‚ฐ
for  i  in  range(edge):
    a, b = map(int, input().split())
    union_parent(a, b)

# ๊ฐ ์›์†Œ๊ฐ€ ์†ํ•ด์žˆ๋Š” ์ง‘ํ•ฉ ์ถœ๋ ฅ
for  i  in  range(1, node + 1):
    print(find_parent(i), end=' ')
print()

# ๋ถ€๋ชจ ํ…Œ์ด๋ธ” ์ถœ๋ ฅ
for  i  in  range(1, node + 1):
    print(parent[i], end=' ')

Weighted Union Find

โœ” parent ๋ฐฐ์—ด์ด ์Œ์ˆ˜์ผ ๊ฒฝ์šฐ ๋ถ€๋ชจ๋…ธ๋“œ๋กœ์„œ ์Œ์ˆ˜์˜ ์ ˆ๋Œ“๊ฐ’์ด size(rank)๊ฐ€ ๋˜๊ณ , ์–‘์ˆ˜์ผ ๊ฒฝ์šฐ ๋ถ€๋ชจ๋…ธ๋“œ๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค

parent = [-1 for _ in range(N + 1)]

def find_parent(x):
    if parent[x] < 0:
        return x
    p = find(parent[x])
    parent[x] = p
    return p

def union_parent(x, y):
    x = find_parent(x)
    y = find_parent(y)

    if x == y:
        return

    if parent[x] < parent[y]:
        parent[x] += parent[y]
        parent[y] = x
    else:
        parent[y] += parent[x]
        parent[x] = y

๋Œ“๊ธ€