요즘 탐욕법을 중점적으로 공부하고 있다. 카테고리에서 탐욕법이라고 쓰여있으면 무작정 풀고 본다. 사실 이전에 알고리즘 공부를 열심히 한 적 있는데, 그 때 만났던 최소 신장 트리 문제를 다시 만났다. 이름만 보면 조금 어려워보인다. 막상 알고나면 간단하고 빠르고 좋다.
최악의 경우 만난 노드만큼 부모를 찾게 되어 findParent 함수의 효율성이 떨어진다. 이를 보완하기 위해 Union-By-Rank 알고리즘으로 탐색 과정을 압축한다.
function findParent(node) {
if (parent[node] === node) return node;
else return findParent(parent[node]);
}
function unionParent(left, right) {
left = findParent(left);
right = findParent(right);
if (left === right) return false;
parent[right] = left;
return true;
}
function solution(n, costs) {
// * union-find 구현
let result = 0;
const parent = Array(n)
.fill(0)
.map((el, idx) => idx);
// * union by rank
const rank = Array(n).fill(0);
function findParent(node) {
if (parent[node] === node) return node;
else return findParent(parent[node]);
}
function unionParent(left, right) {
left = findParent(left);
right = findParent(right);
if (left === right) return false;
if (rank[left] < rank[right]) parent[left] = right;
else {
parent[right] = left;
if (rank[left] === rank[right]) rank[left]++;
}
return true;
}
크루스컬 알고리즘의 시간 복잡도는 간선의 숫자에 비례함.
간선을 정렬하는데 시간 복잡도가 가장 많이 소요된다. 퀵정렬의 시간복잡도는 O(N log N) 이므로 간선에 대입되니 O(E log E)
사이클이 존재하는지 확인하는 과정은 Union-Find, Union-By-Rank 기법으로 인하여 상수에 가까워짐
https://programmers.co.kr/learn/courses/30/lessons/42861
function solution(n, costs) {
// * union-find 구현
let result = 0;
const parent = Array(n)
.fill(0)
.map((el, idx) => idx);
// * union by rank
const rank = Array(n).fill(0);
function findParent(node) {
if (parent[node] === node) return node;
else return findParent(parent[node]);
}
function unionParent(left, right) {
left = findParent(left);
right = findParent(right);
if (left === right) return false;
if (rank[left] < rank[right]) parent[left] = right;
else {
parent[right] = left;
if (rank[left] === rank[right]) rank[left]++;
}
return true;
}
// * cost 순서로 내림차순 정렬
const sortedCosts = costs.slice().sort((a, b) => b[2] - a[2]);
for (let i = 0; i < costs.length; i++) {
let [from, to, cost] = sortedCosts.pop();
if (unionParent(from, to)) result += cost;
}
return result;
}
console.log(
solution(5, [
[0, 1, 1],
[3, 4, 1],
[1, 2, 2],
[2, 3, 4],
])
);
// n / costs / answer
// 5 [[0, 1, 5], [1, 2, 3], [2, 3, 3], [3, 1, 2], [3, 0, 4], [2, 4, 6], [4, 0, 7]] 15
// 5 [[0, 1, 1], [3, 4, 1], [1, 2, 2], [2, 3, 4]] 8
// 4 [[0, 1, 5], [1, 2, 3], [2, 3, 3], [1, 3, 2], [0, 3, 4]] 9
// 5 [[0, 1, 1], [3, 1, 1], [0, 2, 2], [0, 3, 2], [0, 4, 100]] 104
// 6 [[0, 1, 5], [0, 3, 2], [0, 4, 3], [1, 4, 1], [3, 4, 10], [1, 2, 2], [2, 5, 3], [4, 5, 4]] 11
// 5 [[0, 1, 1], [2, 3, 1], [3, 4, 2], [1, 2, 2], [0, 4, 100]] 6
// 5 [[0, 1, 1], [0, 4, 5], [2, 4, 1], [2, 3, 1], [3, 4, 1]] 8
// 5 [[0, 1, 1], [0, 2, 2], [0, 3, 3], [0, 4, 4], [1, 3, 1]] 8
https://ko.wikipedia.org/wiki/크러스컬_알고리즘
https://gmlwjd9405.github.io/2018/08/28/algorithm-mst.html
https://velog.io/@fldfls/최소-신장-트리-MST-크루스칼-프림-알고리즘