[알고리즘] 상호 배타적 집합(Disjoint Set)
글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자를 적어주세요. 글의 요약 설명 부분. 150자입니다
목차
인프런 Rookiss님의 '자료구조와 알고리즘' 강의를 기반으로 정리한 필기입니다.
😎 [C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part3: 자료구조와 알고리즘 강의 들으러 가기!
상호 배타적 집합 (Disjoint Set)
상호 배타적 집합 (Disjoint Set)은 유니온-파인드 Union-Find (합치기-찾기) 방식을 따른다.
다음은 아래의 코드의 예시 상황이다.
Lineage Battleground (혼종!)
'혈맹전 + 서바이벌'
1인 팀 1000명 (팀id 0~999)
동맹 (1번팀 + 2번팀 = 1번팀)
일반적인 벡터 배열을 이용한 방법
찾기 연산 O(1)
합치기 연산 O(N)
void LineageBattleground()
{
struct User
{
int teamId;
};
// TODO : UserManager
vector<User> users;
for (int i = 0; i < 1000; i++)
{
users.push_back(User{ i });
}
// 팀 동맹
// users[1] <-> users[5]
users[5].teamId = users[1].teamId; // 1
// [0][1][2][3][4]...[999]
// [1][1][1][1][1]...[2][2][2][2]...[999]
// teamId=1인 팀과 teamId=2인 팀이 통합
for (User& user : users)
{
if (user.teamId == 1)
user.teamId = 2;
}
}
|
cs |
상호 배타적 집합(Naive Disjoint Set) 방식
class NaiveDisjointSet
{
public:
NaiveDisjointSet(int n) : _parent(n)
{
for (int i = 0; i < n; i++)
_parent[i] = i;
}
// 니 대장이 누구니?
int Find(int u) // u는 최상위 노드
{
if (u == _parent[u])
return u;
return Find(_parent[u]);
}
// u와 v를 합친다 (일단 u가 v 밑으로)
void Merge(int u, int v)
{
u = Find(u);
v = Find(v);
if (u == v)
return;
_parent[u] = v;
}
private:
vector<int> _parent;
};
|
cs |
상호 배타적 집합(Disjoint Set) 방식
트리가 한쪽으로 기우는 문제를 해결할 수 있는 방식이다. 트리를 합칠 때, 항상 [높이가 낮은 트리]를 [높이가 높은 트리] 밑으로 보낸다.
- (Union-By-Rank) 랭크에 의한 합치기 최적화이다.
- 시간 복잡도 O(Ackermann(n)) = O(1)
class DisjointSet
{
public:
DisjointSet(int n) : _parent(n), _rank(n ,1)
{
for (int i = 0; i < n; i++)
_parent[i] = i;
}
// 니 대장이 누구니?
int Find(int u) // u는 최상위 노드
{
if (u == _parent[u])
return u;
//_parent[u] = Find(_parent[u]);
//return _parent[u];
return _parent[u] = Find(_parent[u]); // 위의 두줄의 코드와 같은 의미
}
// u와 v를 합친다
void Merge(int u, int v)
{
u = Find(u);
v = Find(v);
if (u == v)
return;
if (_rank[u] > _rank[v])
swap(u, v);
// rank[u] <= rank[v] 보장됨
_parent[u] = v;
if (_rank[u] == _rank[v])
_rank[v]++;
}
private:
vector<int> _parent;
vector<int> _rank;
};
|
cs |
전체코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
#include <iostream>
#include <vector>
#include <list>
#include <stack>
#include <queue>
using namespace std;
void LineageBattleground()
{
struct User
{
int teamId;
// TODO
};
// TODO : UserManager
vector<User> users;
for (int i = 0; i < 1000; i++)
{
users.push_back(User{ i });
}
// 팀 동맹
// users[1] <-> users[5]
users[5].teamId = users[1].teamId; // 1
// [0][1][2][3][4]...[999]
// [1][1][1][1][1]...[2][2][2][2]...[999]
// teamId=1인 팀과 teamId=2인 팀이 통합
for (User& user : users)
{
if (user.teamId == 1)
user.teamId = 2;
}
// 찾기 연산 O(1)
// 합치기 연산 O(N)
}
// 트리 구조를 이용한 상호 배타적 집합의 표현
// [0] [1] [2] [3] [4] [5]
struct Node
{
Node* leader;
};
// 조직 폭력배 구조?
// [1] [3]
// [2] [4][5]
// [0]
// 시간 복잡도 : 트리의 높이에 비례한 시간이 걸림.
class NaiveDisjointSet
{
public:
NaiveDisjointSet(int n) : _parent(n)
{
for (int i = 0; i < n; i++)
_parent[i] = i;
}
// 니 대장이 누구니?
int Find(int u) // u는 최상위 노드
{
if (u == _parent[u])
return u;
return Find(_parent[u]);
}
// u와 v를 합친다 (일단 u가 v 밑으로)
void Merge(int u, int v)
{
u = Find(u);
v = Find(v);
if (u == v)
return;
_parent[u] = v;
}
private:
vector<int> _parent;
};
// 트리가 한쪽으로 기우는 문제를 해결?
// 트리를 합칠 때, 항상 [높이가 낮은 트리를] [높이가 높은 트리] 밑으로
// (Union-By-Rank) 랭크에 의한 합치기 최적화
// 시간 복잡도 O(Ackermann(n)) = O(1)
class DisjointSet
{
public:
DisjointSet(int n) : _parent(n), _rank(n ,1)
{
for (int i = 0; i < n; i++)
_parent[i] = i;
}
// 니 대장이 누구니?
int Find(int u) // u는 최상위 노드
{
if (u == _parent[u])
return u;
//_parent[u] = Find(_parent[u]);
//return _parent[u];
return _parent[u] = Find(_parent[u]); // 위의 두줄의 코드와 같은 의미
}
// u와 v를 합친다
void Merge(int u, int v)
{
u = Find(u);
v = Find(v);
if (u == v)
return;
if (_rank[u] > _rank[v])
swap(u, v);
// rank[u] <= rank[v] 보장됨
_parent[u] = v;
if (_rank[u] == _rank[v])
_rank[v]++;
}
private:
vector<int> _parent;
vector<int> _rank;
};
int main()
{
DisjointSet teams(1000);
teams.Merge(10, 1);
int teamId = teams.Find(1);
int teamId2 = teams.Find(10);
teams.Merge(3, 2);
int teamId3 = teams.Find(3);
int teamId4 = teams.Find(2);
teams.Merge(1, 3);
int teamId5 = teams.Find(1);
int teamId6 = teams.Find(3);
}
|
cs |
'⭐ Programming > 자료구조와 알고리즘' 카테고리의 다른 글
[알고리즘] 크루스칼 알고리즘(Kruskal Algorithm)을 이용한 길찾기 (0) | 2022.11.07 |
---|---|
[알고리즘] 최소 신장 트리, 크루스칼 알고리즘(Kruskal Algorithm) (0) | 2022.11.07 |
[알고리즘] 해시 테이블 (Hash Table) (0) | 2022.11.06 |
[알고리즘] 퀵 정렬 (Quick Sort) (0) | 2022.11.05 |
[알고리즘] 레드 블랙 트리 (Red Black Tree) (0) | 2022.11.03 |
댓글
이 글 공유하기
다른 글
-
[알고리즘] 크루스칼 알고리즘(Kruskal Algorithm)을 이용한 길찾기
[알고리즘] 크루스칼 알고리즘(Kruskal Algorithm)을 이용한 길찾기
2022.11.07 -
[알고리즘] 최소 신장 트리, 크루스칼 알고리즘(Kruskal Algorithm)
[알고리즘] 최소 신장 트리, 크루스칼 알고리즘(Kruskal Algorithm)
2022.11.07 -
[알고리즘] 해시 테이블 (Hash Table)
[알고리즘] 해시 테이블 (Hash Table)
2022.11.06 -
[알고리즘] 퀵 정렬 (Quick Sort)
[알고리즘] 퀵 정렬 (Quick Sort)
2022.11.05