코딩테스트 합격자 되기 C++
10장 집합
저자 박경록
목차
- 상호배타적 집합이란?
- 집합의 연산
* union연산과 find 연산
* 경로압축 / 랭크기반 알고리즘으로 개선하기
- 집합의 구현
강의영상
상호배타적 집합
- 교집합이 없는 집합관계(집합은 꼭 2개가 아니라 N개일수도 있음)
1
2 3
4
5 6
집합 A 집합 B
상호배타적 집합이다.
집합 A의 원소는 {1,2,3}이고 집합 B의 원소는
{4,5,6} 이므로 교집합이 없음
1
2 3
2
4 6
집합 A 집합 B
상호배타적 집합이 아니다.
집합 A의 원소는 {1,2,3}이고 집합 B의 원소는
{2,4,6} 이므로 교집합 {2}가 존재함
집합 표현하기
무엇을 고려해야 할까요?
- 특정 집합 원소들이 하나의 집합의 원소라는 것을 알 수 있어야 함
* 집합 A = {1,2,3}일때 각 원소가 집합 A에 속한다는 걸 알아야 함
- 각 집합간 다른집합이라는 것을 알 수 있어야 함
* 집합 A = {1,2,3}, B = {4,5,6} 일때 두 집합이 다른 집합이란걸 알아야 함
- 특정 원소가 어느 집합에 속하는지 알수 있어야 함
* 집합 A = {1,2,3}, B = {4,5,6} 일때 원소 5가 집합 B에 속한다는 걸 확인 가능 해야함
- 두개의 집합을 하나로 합칠수 있어야 함
* 집합 A = {1,2,3}, B = {4,5,6} 일때 이를 합쳐서 {1,2,3,4,5,6}만들수 있어야 함
각 집합의 대표원소를 만들면 해결 된다.
집합 표현하기
1
2 3
4
5 6
집합 A 집합 B
각 집합에서 가장 작은 원소를 대
표원소(루트노드)로 한다.
1 2 3 4 5 6
1 1 1 4 4 4
대표원소 : 1 대표원소 : 4
- 대표원소는 현재노드와 부모노드가 같음
- 배열을 활용해서 나타낼 수 있음(현재노드는 인덱스, 부모노드는 값)
- 부모노드가 없다면 -1로 나타낼수 있음
현재노드
부모노드
8
7
-1
8
3
집합의 연산 find
특정 노드의 루트 노드를 확인하는 연산
- 특정 노드로 부터, 루트노드가 나올때 까지 거슬러 올라가면 됨
- union 연산에서도 활용 됨
집합의 연산 find의 최악의 경우
1
2
3
4
5
1 2 3 4 5
1 1 2 4
현재노드
부모노드 3
- 루트노드를 찾는데 필요한 경로가 깊어질경우 연산횟수가 증가함 O(N)
- 어떻게 하면 경로의 깊이를 줄일수 있을까??
=> 경로 압축 알고리즘을 활용하자!
집합의 연산 find(경로압축이란?)
1
2
3
4
5
1
2 3 4 5
집합은 하나이고 모든 노드의 루트노드는 1이다.
오른쪽 처럼 바꾸면 경로의 최대 깊이는 1이 됨
집합의 연산 find(경로압축의 과정)
find연산을 하는 노드가 루트노드 일 경우, 루트노드를 반환
find연산을 하는 노드가 루트노드가 아닐 경우, 자신의 부모노드를 find(해당 노드의
부모노드)로 설정
1
2
3
4
5
실제 함수 호출 과정
find(5) , disjoint[5] = disjoint[4] 이후, disjoint[5] 반환
> find(4), disjoint[4] = disjoint[3] 이후, disjoint[4] 반환
> find(3), disjoint[3] = disjoint[2] 이후, disjoint[3] 반환
> find(2), disjoint[2] = disjoint[1] 이후, disjoint[2] 반환
> find(1), 루트노드 이므로 disjoint[1] 반환
집합의 연산 find(경로압축의 실제동작 1)
1
2
3
4
5
①
②
③
④
루트노드 확인
1
2
3
4
5
① disjoint[2] = disjoint[1]
1 2 3 4 5
1 1
2
1
4
1
현재노드
부모노드
3
1
② disjoint[3] = disjoint[2]
③ disjoint[4] = disjoint[3]
④ disjoint[5] = disjoint[4]
① ② ③ ④
disjoint
집합의 연산 find(경로압축의 실제동작 1, 결론적으로)
1
2
3
4
5
① disjoint[2] = disjoint[1]
1 2 3 4 5
1 1
2
1
4
1
현재노드
부모노드
3
1
② disjoint[3] = disjoint[2]
③ disjoint[4] = disjoint[3]
④ disjoint[5] = disjoint[4]
① ② ③ ④
disjoint
1
2 3 4 5
집합의 연산 find(경로압축의 실제동작 2)
1
2
4
루트노드 확인
3 5
6
7
①
②
③
1
2
4
3 5
6
7
① disjoint[2] = disjoint[1]
② disjoint[6] = disjoint[2]
③ disjoint[7] = disjoint[6]
집합의 연산 find(경로압축의 실제동작 2)
1
2
4
3 5
6
7
① disjoint[2] = disjoint[1]
② disjoint[6] = disjoint[2]
③ disjoint[7] = disjoint[6]
1
2
4
3 5 6 7
경로 압축을 했다고, 루트노드를 제외한 모든 노드의 깊이가 1이 되는건 아님.
find 연산을 할 때, 루트노드를 찾는 과정에서 거쳐간 노드들의 경로가 압축 됨
경로압축을 적용하면 find연산의 시간복잡도는 O(1)에 가까워짐
집합의 연산(union)
두 개의 집합을 하나의 집합으로 합치는 연산
find 연산을 통해 각 집합의 대표원소를 구하고, 루트노드를 하나로 통일
=> 여러가지 방법이 있지만 작은 루트노드쪽으로 합치는 방법도 있음
1
2 3
4
5 6
집합 A 집합 B
1
2 3 4
5 6
집합 A 집합 B
1 < 4
집합의 연산(union, 세부과정)
1
2 3
4
5 6
집합 A 집합 B
8
1
1
현재노드
부모노드
2
1
3
1
4
4
1
5
4
6
4
7
-1
8
3
1
union(6,8) 수행
=>find(6), find(8)을 통해 루트노드를 확인
=> find(8) = 1, find(6) = 4 이므로 find(8) < find(6)
=> 이 두 집합의 대표원소를 find(8)로 통일
이 과정에서 구현에 따라 경로압축이 적용될 수 있음!
find(8)수행시 경로압축이 되면서 수정 됨
집합의 연산(union, 효율성에 대한 고민)
union 연산이후 깊이를 최소화 할 수 있을까?
=> 집합에 랭크를 도입하자(특정노드 기준 최대 깊이)
=> 랭크가 더 큰쪽으로 합쳐서, 랭크의 증가를 최소화 하자(랭크 기반 알고리즘)
집합의 연산(union, 랭크기반 알고리즘)
1
2
4
3 5
6
7 0
0 1
0
2
0
3
랭크 개념에 대해 알아보자!
=> 현재 노드 기준 트리 최대 깊이
=> 노드{1}의 경우 2->1->0이 최대 깊이이므로 랭크는 3
=> 노드 {3},{4}{5},{7}은 자신을 제외하고는 더이상 내려갈 곳이 없으므로 랭크 0
=> 노드{2}의 경우 6->7이 최대 깊이 이므로 랭크는 2
=> 루트노드의 랭크가 가장 높다 랭크기반 알고리즘은 루트노드의 랭크 기준
=> 루트노드의 랭크를 낮게 유지하는 방향으로 합치는 것이 전략
집합의 연산(union, 랭크기반 알고리즘)
어떻게 합쳐야 하나?
=> 랭크가 더 높은쪽으로 합쳐야 함
1
2
4
3
5
6 7
2 1
1
2
4
3 5
6 7
1
2
4
3
5
6 7
랭크가 더 높은 쪽으로 합친 경우
랭크가 더 낮은 쪽으로 합친 경우
집합의 구현
- 노드의 최대값이 크지 않은경우 => 배열로 구현
- 노드의 최대값이 큰 경우 => unordered_map으로 구현
- 대부분의 코테 문제는 경로압축,랭크기반 까지 구현할 필요는 없습니다.
union &find
array / fast
union &find
array
union &find
unordered_
map
언제 집합알고리즘을 쓸까요?
이미지 세그멘테이션
- 각 이미지를 의미있는 부분으로 분할
- 아래 이미지를 보면 이미지에서 각 객체마다 분할해서 다른색을 칠하고 있음
언제 집합알고리즘을 쓸까요?
네트워크 연결성 확인
- 대규모 네트워크에서 두 컴퓨터가 서로 연결되어있는지 확인
B
C
D
E
A
F
G H
알고리즘 문제 예시
네트워크 연결성 확인
주어진 노드와 연결된 간선으로 이루어진 네트워크에서 두 노드가 서로 연결되어 있는지 확
인하는 문제. 노드의 개수, 간선 목록, 그리고 쿼리 목록이 주어집니다.
입력값
5 ← 노드의 최대값
3 ← 간선의 개수
0 1
1 2
3 4
2 ← 쿼리의 개수
0 2
0 3
0
1 2
3
4
출력값
1 ← 연결됨(0,2)
0 ← 연결되지 않음(0,3)
0,2 연결됨
0,3 연결되지 않음
커뮤니티 소개(오픈카톡방) cont’d
- 저자 직접 운영
- 대략 400명 정도 인원이 자유롭게 소통
- 개인적인 주제 및 코딩 관련 소통 가능
커뮤니티 소개(오픈카톡방)
커뮤니티 소개(깃허브) cont’d
- 책 문제의 정답코드
- 코딩테스트 준비를 위해 필요한 자료구조 및 알고리즘
- C++ 기본 문법
- STL의 성능 비교
커뮤니티 소개(깃허브)
커뮤니티 소개(디스코드) cont’d
- 모각코 진행
- 공식 스터디 / 개인 스터디 모집 공고 및 진행
- 책 관련 문의 / 답변
커뮤니티 소개(디스코드)

코딩 테스트 합격자 되기 C++ 10장 집합에 대한 강의 자료 입니다.

  • 1.
    코딩테스트 합격자 되기C++ 10장 집합 저자 박경록
  • 2.
    목차 - 상호배타적 집합이란? -집합의 연산 * union연산과 find 연산 * 경로압축 / 랭크기반 알고리즘으로 개선하기 - 집합의 구현
  • 3.
  • 4.
    상호배타적 집합 - 교집합이없는 집합관계(집합은 꼭 2개가 아니라 N개일수도 있음) 1 2 3 4 5 6 집합 A 집합 B 상호배타적 집합이다. 집합 A의 원소는 {1,2,3}이고 집합 B의 원소는 {4,5,6} 이므로 교집합이 없음 1 2 3 2 4 6 집합 A 집합 B 상호배타적 집합이 아니다. 집합 A의 원소는 {1,2,3}이고 집합 B의 원소는 {2,4,6} 이므로 교집합 {2}가 존재함
  • 5.
    집합 표현하기 무엇을 고려해야할까요? - 특정 집합 원소들이 하나의 집합의 원소라는 것을 알 수 있어야 함 * 집합 A = {1,2,3}일때 각 원소가 집합 A에 속한다는 걸 알아야 함 - 각 집합간 다른집합이라는 것을 알 수 있어야 함 * 집합 A = {1,2,3}, B = {4,5,6} 일때 두 집합이 다른 집합이란걸 알아야 함 - 특정 원소가 어느 집합에 속하는지 알수 있어야 함 * 집합 A = {1,2,3}, B = {4,5,6} 일때 원소 5가 집합 B에 속한다는 걸 확인 가능 해야함 - 두개의 집합을 하나로 합칠수 있어야 함 * 집합 A = {1,2,3}, B = {4,5,6} 일때 이를 합쳐서 {1,2,3,4,5,6}만들수 있어야 함 각 집합의 대표원소를 만들면 해결 된다.
  • 6.
    집합 표현하기 1 2 3 4 56 집합 A 집합 B 각 집합에서 가장 작은 원소를 대 표원소(루트노드)로 한다. 1 2 3 4 5 6 1 1 1 4 4 4 대표원소 : 1 대표원소 : 4 - 대표원소는 현재노드와 부모노드가 같음 - 배열을 활용해서 나타낼 수 있음(현재노드는 인덱스, 부모노드는 값) - 부모노드가 없다면 -1로 나타낼수 있음 현재노드 부모노드 8 7 -1 8 3
  • 7.
    집합의 연산 find 특정노드의 루트 노드를 확인하는 연산 - 특정 노드로 부터, 루트노드가 나올때 까지 거슬러 올라가면 됨 - union 연산에서도 활용 됨
  • 8.
    집합의 연산 find의최악의 경우 1 2 3 4 5 1 2 3 4 5 1 1 2 4 현재노드 부모노드 3 - 루트노드를 찾는데 필요한 경로가 깊어질경우 연산횟수가 증가함 O(N) - 어떻게 하면 경로의 깊이를 줄일수 있을까?? => 경로 압축 알고리즘을 활용하자!
  • 9.
    집합의 연산 find(경로압축이란?) 1 2 3 4 5 1 23 4 5 집합은 하나이고 모든 노드의 루트노드는 1이다. 오른쪽 처럼 바꾸면 경로의 최대 깊이는 1이 됨
  • 10.
    집합의 연산 find(경로압축의과정) find연산을 하는 노드가 루트노드 일 경우, 루트노드를 반환 find연산을 하는 노드가 루트노드가 아닐 경우, 자신의 부모노드를 find(해당 노드의 부모노드)로 설정 1 2 3 4 5 실제 함수 호출 과정 find(5) , disjoint[5] = disjoint[4] 이후, disjoint[5] 반환 > find(4), disjoint[4] = disjoint[3] 이후, disjoint[4] 반환 > find(3), disjoint[3] = disjoint[2] 이후, disjoint[3] 반환 > find(2), disjoint[2] = disjoint[1] 이후, disjoint[2] 반환 > find(1), 루트노드 이므로 disjoint[1] 반환
  • 11.
    집합의 연산 find(경로압축의실제동작 1) 1 2 3 4 5 ① ② ③ ④ 루트노드 확인 1 2 3 4 5 ① disjoint[2] = disjoint[1] 1 2 3 4 5 1 1 2 1 4 1 현재노드 부모노드 3 1 ② disjoint[3] = disjoint[2] ③ disjoint[4] = disjoint[3] ④ disjoint[5] = disjoint[4] ① ② ③ ④ disjoint
  • 12.
    집합의 연산 find(경로압축의실제동작 1, 결론적으로) 1 2 3 4 5 ① disjoint[2] = disjoint[1] 1 2 3 4 5 1 1 2 1 4 1 현재노드 부모노드 3 1 ② disjoint[3] = disjoint[2] ③ disjoint[4] = disjoint[3] ④ disjoint[5] = disjoint[4] ① ② ③ ④ disjoint 1 2 3 4 5
  • 13.
    집합의 연산 find(경로압축의실제동작 2) 1 2 4 루트노드 확인 3 5 6 7 ① ② ③ 1 2 4 3 5 6 7 ① disjoint[2] = disjoint[1] ② disjoint[6] = disjoint[2] ③ disjoint[7] = disjoint[6]
  • 14.
    집합의 연산 find(경로압축의실제동작 2) 1 2 4 3 5 6 7 ① disjoint[2] = disjoint[1] ② disjoint[6] = disjoint[2] ③ disjoint[7] = disjoint[6] 1 2 4 3 5 6 7 경로 압축을 했다고, 루트노드를 제외한 모든 노드의 깊이가 1이 되는건 아님. find 연산을 할 때, 루트노드를 찾는 과정에서 거쳐간 노드들의 경로가 압축 됨 경로압축을 적용하면 find연산의 시간복잡도는 O(1)에 가까워짐
  • 15.
    집합의 연산(union) 두 개의집합을 하나의 집합으로 합치는 연산 find 연산을 통해 각 집합의 대표원소를 구하고, 루트노드를 하나로 통일 => 여러가지 방법이 있지만 작은 루트노드쪽으로 합치는 방법도 있음 1 2 3 4 5 6 집합 A 집합 B 1 2 3 4 5 6 집합 A 집합 B 1 < 4
  • 16.
    집합의 연산(union, 세부과정) 1 23 4 5 6 집합 A 집합 B 8 1 1 현재노드 부모노드 2 1 3 1 4 4 1 5 4 6 4 7 -1 8 3 1 union(6,8) 수행 =>find(6), find(8)을 통해 루트노드를 확인 => find(8) = 1, find(6) = 4 이므로 find(8) < find(6) => 이 두 집합의 대표원소를 find(8)로 통일 이 과정에서 구현에 따라 경로압축이 적용될 수 있음! find(8)수행시 경로압축이 되면서 수정 됨
  • 17.
    집합의 연산(union, 효율성에대한 고민) union 연산이후 깊이를 최소화 할 수 있을까? => 집합에 랭크를 도입하자(특정노드 기준 최대 깊이) => 랭크가 더 큰쪽으로 합쳐서, 랭크의 증가를 최소화 하자(랭크 기반 알고리즘)
  • 18.
    집합의 연산(union, 랭크기반알고리즘) 1 2 4 3 5 6 7 0 0 1 0 2 0 3 랭크 개념에 대해 알아보자! => 현재 노드 기준 트리 최대 깊이 => 노드{1}의 경우 2->1->0이 최대 깊이이므로 랭크는 3 => 노드 {3},{4}{5},{7}은 자신을 제외하고는 더이상 내려갈 곳이 없으므로 랭크 0 => 노드{2}의 경우 6->7이 최대 깊이 이므로 랭크는 2 => 루트노드의 랭크가 가장 높다 랭크기반 알고리즘은 루트노드의 랭크 기준 => 루트노드의 랭크를 낮게 유지하는 방향으로 합치는 것이 전략
  • 19.
    집합의 연산(union, 랭크기반알고리즘) 어떻게 합쳐야 하나? => 랭크가 더 높은쪽으로 합쳐야 함 1 2 4 3 5 6 7 2 1 1 2 4 3 5 6 7 1 2 4 3 5 6 7 랭크가 더 높은 쪽으로 합친 경우 랭크가 더 낮은 쪽으로 합친 경우
  • 20.
    집합의 구현 - 노드의최대값이 크지 않은경우 => 배열로 구현 - 노드의 최대값이 큰 경우 => unordered_map으로 구현 - 대부분의 코테 문제는 경로압축,랭크기반 까지 구현할 필요는 없습니다. union &find array / fast union &find array union &find unordered_ map
  • 21.
    언제 집합알고리즘을 쓸까요? 이미지세그멘테이션 - 각 이미지를 의미있는 부분으로 분할 - 아래 이미지를 보면 이미지에서 각 객체마다 분할해서 다른색을 칠하고 있음
  • 22.
    언제 집합알고리즘을 쓸까요? 네트워크연결성 확인 - 대규모 네트워크에서 두 컴퓨터가 서로 연결되어있는지 확인 B C D E A F G H
  • 23.
    알고리즘 문제 예시 네트워크연결성 확인 주어진 노드와 연결된 간선으로 이루어진 네트워크에서 두 노드가 서로 연결되어 있는지 확 인하는 문제. 노드의 개수, 간선 목록, 그리고 쿼리 목록이 주어집니다. 입력값 5 ← 노드의 최대값 3 ← 간선의 개수 0 1 1 2 3 4 2 ← 쿼리의 개수 0 2 0 3 0 1 2 3 4 출력값 1 ← 연결됨(0,2) 0 ← 연결되지 않음(0,3) 0,2 연결됨 0,3 연결되지 않음
  • 24.
    커뮤니티 소개(오픈카톡방) cont’d -저자 직접 운영 - 대략 400명 정도 인원이 자유롭게 소통 - 개인적인 주제 및 코딩 관련 소통 가능
  • 25.
  • 26.
    커뮤니티 소개(깃허브) cont’d -책 문제의 정답코드 - 코딩테스트 준비를 위해 필요한 자료구조 및 알고리즘 - C++ 기본 문법 - STL의 성능 비교
  • 27.
  • 28.
    커뮤니티 소개(디스코드) cont’d -모각코 진행 - 공식 스터디 / 개인 스터디 모집 공고 및 진행 - 책 관련 문의 / 답변
  • 29.