서로소 집합은 중복되는 원소나 교집합이 없게끔 자료를 저장하는 자료구조를 의미한다. 그래프와 연관되는 개념이지만 서로소 집합은 트리 개념(Root-Parent-Child)을 따르는 것이 특징이다.
root
를 서로 같게 만들어줍니다.root
를 찾기 위해 find()
연산을 사용한다.root
정보가 고정됩니다.1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 |
(1, 2), (1, 3), (3, 5), (6, 4)
가 입력됐다.union()
연산을 수행한다. 1
이고, 2번 정점의 집합이 2
이므로 둘 다 정점 번호와 집합 번호가 같다.1
이 2
보다 작으므로 (우선순위가 크므로) 2
를 1
로 변경한다.union()
연산을 수행한다.1
번 집합에, 3번 정점은 3
번 집합에 있으므로 둘 다 정점 번호와 집합 번호가 같다.1
이 3
보다 작으므로 (우선순위가 크므로) 2
를 1
로 변경한다.union()
연산을 수행한다.1
번 집합에, 5번 정점은 5
번 집합에 있으므로 3번 정점이 집합 번호와 다르다.find()
연산을 해서 3번 -> 1번
으로 이동한다.1
이므로 find()
연산을 종료하고 1
을 반환한다.1
이 5
보다 작으므로 (우선순위가 크므로) 5
를 1
로 변경한다.1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|
1 | 1 | 1 | 4 | 1 | 4 |
#include <iostream>
#include <vector>
using namespace std;
// 재귀적으로 따라가면서
int findRoot(vector<int>& parentArr, int x) {
if (parentArr[x] == x) return x;
return parentArr[x] = findRoot(parentArr, parentArr[x]);
}
// 두 정점의 parent 또는 root를 찾고 같은 root(집합)에 소속시킨다.
void unionOperation(vector<int>& parentArr, int lhs, int rhs) {
lhs = findRoot(parentArr, lhs), rhs = findRoot(parentArr, rhs);
parentArr[rhs] = lhs;
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int V, E; cin >> V >> E;
// 정점들의 root(또는 소속집합) 정보를 기록할 parentArr 배열.
vector<int> parentArr(V + 1);
// 모든 정점을 서로 다른 root(집합)에 소속시킨다.
for (int i = 1; i <= V; ++i) parentArr[i] = i;
// 간선을 입력받고 union() 연산을 수행한다.
for (int i = 0; i < E; ++i) {
int s, e; cin >> s >> e;
unionOperation(parentArr, s, e);
}
// [Option]:: 각 정점들의 root 정보를 출력한다.
for (int i = 1; i <= V; ++i)
cout << findRoot(parentArr, i) << '\t';
cout << '\n';
// [Oprtion]:: 각 정점들의 parent 정보를 출력한다.
for (int i = 1; i <= V; ++i)
cout << parentArr[i] << '\t';
cout << '\n';
}
크루스칼 알고리즘은 간선에 weight
이 존재하는 무방향 그래프에 대해 모든 정점을 연결하면서 간선의 weight
합이 최소가 되는 최소 신장 트리를 구하는 알고리즘이다. 시간 복잡도는 이다.
weight
을 오름차순으로 정렬한다.12 → 18 → 20 → 22 → 24 → 35 → 38 → 41 → 60
으로 정렬된다.find()
연산을 통해 서로 같은 root에 포함된다면 사이클이 발생하는 것이다!find()
연산을 통해 서로 다른 root에 있다면 union()
연산을 수행한다.#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int findRoot(vector<int>& parentArr, int x) {
if (parentArr[x] != x) return findRoot(parentArr, parentArr[x]);
return parentArr[x];
}
void unionOperation(vector<int>& parentArr, int lhs, int rhs) {
lhs = findRoot(parentArr, lhs), rhs = findRoot(parentArr, rhs);
(lhs < rhs) ? (parentArr[rhs] = lhs) : (parentArr[lhs] = rhs);
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int V, E; cin >> V >> E;
// 정점들의 root(또는 소속집합) 정보를 기록할 parentArr 배열.
// 모든 정점을 서로 다른 root(집합)에 소속시킨다.
vector<int> parentArr(V + 1);
for (int i = 1; i <= V; ++i) parentArr[i] = i;
// 간선을 입력받고 mst 배열에 넣는다.
vector<pair<int, pair<int, int>>> mst;
for (int i = 0; i < E; ++i) {
int s, e, w; cin >> s >> e >> w;
mst.push_back({w, {s, e}});
}
// 간선의 weight을 오름차순으로 정렬한다.
sort(begin(mst), end(mst));
cout << '\n';
for (const auto& itr : mst)
cout << '(' << itr.second.first << ", " << itr.second.second << ") = " << itr.first << '\n';
int sum = 0;
for (const auto& element : mst) {
if (findRoot(parentArr, element.second.first) != findRoot(parentArr, element.second.second)) {
unionOperation(parentArr, element.second.first, element.second.second);
sum += element.first;
}
}
cout << "MST의 value = " << sum << '\n';
}
방향 그래프에 대해 방향성을 거스르지 않으면서 정점들을 정렬하는 알고리즘
알고리즘
수업을 듣기 위해서는 자료구조
수업과 객체지향 프로그래밍
수업을 이수해야 한다고 생각해보자.
그러면 자료구조
와 객체지향
은 항상 알고리즘
의 앞에 와야 한다.
이러한 방향성을 유지하면서 정점들을 나열하는 알고리즘을 위상정렬이라고 한다.
indegree
를 계산한다.indegree
값이 0인 점을 시작 정점으로 queue
에 넣고 BFS
를 수행한다.queue
에서 pop()
되는 원소들을 외부 컨테이너에 순서대로 저장한다.BFS
를 수행할 때, 간선에 연결되는 정점은 indegree
를 하나 감소시킨다. 이때 0이 된다면 queue
에 push()
한다.#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int V, E, indegree[1001];
vector<int> graph[1001];
void topologySort() {
vector<int> answer;
queue<int> q;
for (int i = 1; i <= V; ++i)
if (indegree[i] == 0) q.push(i); // indegree가 0인 점을 시작점으로 push한다.
while(!q.empty()) {
int curVertex = q.front(); q.pop();
answer.push_back(curVertex);
for (const int& next : graph[curVertex]) {
indegree[next]--;
if (indegree[next] == 0) q.push(next);
}
}
for (const int& ele : answer)
cout << ele << ' ';
cout << '\n';
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> V >> E;
for (int i = 0; i < E; ++i) {
int s, e; cin >> s >> e;
graph[s].push_back(e);
indegree[e]++; // 종점에 대해 indegree를 하나 증가한다.
}
topologySort();
}