이코테-chapter10: 그래프 이론-서로조 집합 자료구조

cosmos·2022년 3월 28일
0
post-thumbnail

서로소 집합 구조

정의 & 특징

  • Disjoint Sets이란 공통 원소가 없는 두 집합을 의미.
  • 서로소 부분 집합들로 나누어진 원소들의 데이터를 처리하기 위한 자료구조
  • union(합집합), find(찾기) 2개의 연산으로 나뉨.
  • union: 2개의 원소가 포함된 집합을 하나의 집합으로 합치는 연산
  • find: 특정한 원소가 속한 집합이 어떤 집합인지 알려주는 연산
  • 서로소 집합 자료구조는 union-find 자료구조라고도 불린다.
  • 트리 자려구조를 이용하여 집합을 표현하는데 계산 알고리즘 방식은 아래와 같다.
  1. union 연산을 확인하여, 서로 연결된 두 노드 a, b를 확인한다.
  2. a와 b의 루트 노드 a', b'를 각각 찾는다.
  3. a'를 b'의 부모 노드로 설정한다. (b'가 a'를 가르키도록 한다.)
  4. 모든 union 연산을 처리할 때까지 1, 2, 3번 과정을 반복한다.
  • 3의 과정에서 더 번호가 작은 원소가 부모 노드가 되도록 구현하는 경우가 대부분이다.

기본적인 서로소 집합 알고리즘 소스코드

# 특정 원소가 속한 집합을 찾기
def find_parent(parent, x):
    # 루트 노드가 아니라면, 루트 노드를 찾을 때까지 재귀적으로 호출
    if parent[x] != x:
        return find_parent(parent, parent[x])
    
    return parent[x]  # 경로 압축 기법

# 두 원소가 속한 집합을 합치기
def union_parent(parent, a, b):
    a = find_parent(parent, a)
    b = find_parent(parent, b)

    if a < b:
        parent[b] = a
    else:
        parent[a] = b

# 노드의 개수와 간선(union 연산)의 개수 입력받기
v, e = map(int, input().split())
parent = [0] * (v + 1)

# 부모 테이블상에서, 부모를 자기 자신으로 초기화
for i in range(1, v+1):
    parent[i] = i

# union 연산을 각각 수행
for i in range(e):
    a, b = map(int, input().split())
    union_parent(parent, a, b)

# 각 원소가 속한 집합 출력
print('각 원소가 속한 집합: ', end='')

for i in range(1, v+1):
    print(find_parent(parent, i), end=' ')

print()

# 부모 테이블 내용 출력
print('부모 테이블: ', end='')

for i in range(1, v+1):
    print(parent[i], end=' ')

시간 복잡도

  • 경로 압축 방법만을 사용하며 노드의 개수가 v, 최대 v-1개의 union 연산과 m개의 find 연산이 가능하면
  • 시간 복잡도는 O(V + M(1+log(2-M/V)V))이다.

서로소 집합을 활용한 사이클 판별

  • 무방향 그래프내에서의 사이클 판별할 때 사용한다는 특징이 있다.
  • 방향 그래프에서의 사이클 여부는 DFS를 이용하여 판별할 수 있다.
  • 사이클을 판별하는 방식은 아래와 같다.
  1. 각 간선을 확인하며 두 노드의 루트 노드를 확인한다.
  2. 루트 노드가 서로 다르다면 두 노드에 대하여 union 연산을 수행한다.
  3. 루트 노드가 서로 같다면 사이클이 발생한 것이다.
  4. 그래프에 포함되어 있는 모든 간선에 대하여 1, 2, 3번 과정을 반복한다.

서로소 집합을 활용한 사이클 판별 소스코드

# 특정 원소가 속한 집합을 찾기
def find_parent(parent, x):
    # 루트 노드가 아니라면, 루트 노드를 찾을 때까지 재귀적으로 호출
    if parent[x] != x:
        parent[x] = find_parent(parent, parent[x])
    return parent[x]

# 두 원소가 속한 집합을 합치기
def union_parent(parent, a, b):
    a = find_parent(parent, a)
    b = find_parent(parent, b)

    if a < b:
        parent[b] = a
    else:
        parent[a] = b

# 노드의 개와 간선의 개수 입력받기
v, e = map(int, input().split())
parent = [0] * (v + 1)  # 부모 테이블 초기화

# 부모 테이블상에서, 부모를 자기 자신으로 초기화
for i in range(1, v+1):
    parent[i] = i

cycle = False  # 사이클 발생 여부

for i in range(e):
    a, b = map(int, input().split())
    # 사이클이 발생한 경우 종료
    if find_parent(parent, a) == find_parent(parent, b):
        cycle = True
        break
    # 사이클이 발생하지 않았다면 합집합 수행
    else:
        union_parent(parent, a, b)

if cycle:
    print("사이클이 발생했습니다.")
else:
    print("사이클이 발생하지 않았습니다.")

출처 & 깃허브

이것이 취업을 위한 코딩 테스트다 with python

github

0개의 댓글

관련 채용 정보