- Find: 최악의 경우 이지만 경로 압축(Path Compression)을 사용하면 거의 에 가깝게 동작합니다.
- Union: 의 시간 복잡도를 나타내고있습니다. 두 트리의 루트를 찾고 하나를 다른 하나에 연결하는 간단한 작업이므로 매우 빠릅니다.
- $O(N)의 공간 복잡도를 나타냅니다. 여기서 N은 원소의 개수입니다.
Disjoint Set
자료구조는 특히 연결 요소가 동적으로 변하지 않는 상황에서 효과적입니다. 자주 변하는 경우에는 추가 작업이 필요할 수 있습니다. Find
연산 시, 찾은 루트 노드를 각 노드의 부모로 직접 설정하여, 트리의 높이를 줄입니다. 이를 통해 다음 Find
연산의 속도를 높입니다. Find
연산을 수행하면서 거쳐가는 모든 노드들의 부모를 해당 집합의 루트 노드로 직접 연결함으로써, 다음 Find
연산의 효율성을 크게 향상 시킵니다. Find
연산의 평균 시간 복잡도가 거의 에 가깝게 됩니다. 이는 각 Find
연산 후에 트리 구조가 점점 더 플릿해지기 때문입니다. Find
연산은 대부분 루트 노드에 매운 가까운 위치에서 시작되므로 매우 빠르게 처리됩니다.struct DisjointSet {
var parent: [Int]
var rank: [Int]
init(count: Int) {
// 자기 자신이 부모가 될 수 있도록 초기화 합니다.
parent = Array(0..<count)
// rank를 모두 0으로 초기화 합니다.
rank = Array(repeating: 0, count: count)
}
mutating func find(_ x: Int) -> Int {
if parent[x] != x {
// 경로 압축 (둘 다 가능)
parent[x] = find(parent[x])
// parent[x] = find(parent[parent[x]])
}
return parent[x]
}
// Rank가 높은 노드가 부모가 됩니다.
mutating func unionByRank(_ x: Int, _ y: Int) {
let xRoot = find(x)
let yRoot = find(y)
// 각 부모가 다를때 실행
if xRoot != yRoot {
// 랭크가 낮은 집합을 합칩니다. 즉, 랭크가 높은 값이 루트가 됩니다.
if rank[xRoot] < rank[yRoot] {
parent[xRoot] = yRoot
} else if rank[xRoot] > rank[yRoot] {
parent[yRoot] = xRoot
// 랭크가 같을 경우, 한 쪽을 다른 쪽의 부모로 만들고 해당 부모의 랭크를 1 증가시킵니다.
} else {
parent[yRoot] = xRoot
rank[xRoot] += 1
}
}
}
// 집합안에서 가장 낮은 숫자가 부모가 됩니다.
mutating func unionByAscending(_ x: Int, _ y:Int) {
let xRoot = find(x)
let yRoot = find(y)
if xRoot != yRoot {
if rank[xRoot] < rank[yRoot] {
parent[xRoot] = yRoot
} else if rank[xRoot] > rank[yRoot] {
parent[yRoot] = xRoot
// 두 랭크가 같을 때 숫자가 낮은 노드를 부모로 설정합니다.
} else {
if xRoot < yRoot {
parent[yRoot] = xRoot
rank[xRoot] += 1
} else {
parent[xRoot] = yRoot
rank[yRoot] += 1
}
}
}
}
}
let n = 6
var disjointSet = DisjointSet(count: n + 1)
let edges = [(1,4), (2,3), (2,4), (5,6)]
for edge in edges {
disjointSet.unionByAscending(edge.0, edge.1)
}
var groups: [Int: [Int]] = [:]
for i in 1...n {
let root = disjointSet.find(i)
groups[root, default: []].append(i)
}
print(groups)
print(disjointSet.parent)
print(disjointSet.rank)
[5: [5, 6], 1: [1, 2, 3, 4]]
[0, 1, 1, 1, 1, 5, 5]
[0, 2, 1, 0, 0, 1, 0]
YES
" 또는 "yes
"를, 그렇지 않다면 "NO
" 또는 "no
"를 한 줄에 하나씩 출력한다.7 8
0 1 3
1 1 7
0 7 6
1 7 1
0 3 7
0 4 2
0 1 1
1 1 1
NO
NO
YES
import Foundation
struct DisjointSet {
var parent: [Int]
var rank: [Int]
init(count: Int) {
// n+1 까지 집합을 만들기 위해
parent = Array(0...count)
rank = Array(repeating: 0, count: count + 1)
}
mutating func find(_ x: Int) -> Int {
if parent[x] != x {
parent[x] = find(parent[x])
}
return parent[x]
}
mutating func union(_ x: Int, _ y: Int) {
let xRoot = find(x)
let yRoot = find(y)
if xRoot != yRoot {
if rank[xRoot] < rank[yRoot] {
parent[xRoot] = yRoot
} else if rank[xRoot] > rank[yRoot] {
parent[yRoot] = xRoot
} else {
parent[yRoot] = xRoot
rank[xRoot] += 1
}
}
}
}
let testCase = readLine()!.components(separatedBy: " ").map { Int($0)! }
let n = testCase[0]
let m = testCase[1]
var disjointSet = DisjointSet(count: n)
for _ in 0..<m {
let testCase = readLine()!.components(separatedBy: " ").map { Int($0)! }
let check = testCase[0]
let a = testCase[1]
let b = testCase[2]
if check == 0 {
disjointSet.union(a, b)
} else {
let x = disjointSet.find(a)
let y = disjointSet.find(b)
// 이 부분에서 오타 발생으로 두번 틀림
if x == y {
print("YES")
} else {
print("NO")
}
}
}
첫 줄에 도시의 수 N이 주어진다. N은 200이하이다. 둘째 줄에 여행 계획에 속한 도시들의 수 M이 주어진다. M은 1000이하이다. 다음 N개의 줄에는 N개의 정수가 주어진다. i번째 줄의 j번째 수는 i번 도시와 j번 도시의 연결 정보를 의미한다. 1이면 연결된 것이고 0이면 연결이 되지 않은 것이다. A와 B가 연결되었으면 B와 A도 연결되어 있다. 마지막 줄에는 여행 계획이 주어진다. 도시의 번호는 1부터 N까지 차례대로 매겨져 있다.
3
3
0 1 0
1 0 1
0 1 0
1 2 3
YES
import Foundation
struct DisjointSet {
var parent: [Int]
var rank: [Int]
init(count: Int) {
parent = Array(0..<count)
rank = Array(repeating: 0, count: count)
}
mutating func find(_ x: Int) -> Int {
if parent[x] != x {
parent[x] = find(parent[x])
}
return parent[x]
}
mutating func union(_ x: Int, _ y: Int) {
let xRoot = find(x)
let yRoot = find(y)
if xRoot != yRoot {
if rank[xRoot] < rank[yRoot] {
parent[xRoot] = yRoot
} else if rank[xRoot] > rank[yRoot] {
parent[yRoot] = xRoot
} else {
parent[yRoot] = xRoot
rank[xRoot] += 1
}
}
}
}
// 도시의 수
let n = Int(readLine()!)!
// 여행갈 도시의 수
let m = Int(readLine()!)!
var disjointSet = DisjointSet(count: n)
for i in 0..<n {
let testCase = readLine()!.components(separatedBy: " ").map { Int($0)! }
for j in 0..<n {
// 해당 부분에서 testCase[j] == 1가 아닌 j == 1로 해서 틀렸음
if testCase[j] == 1 {
disjointSet.union(i, j)
}
}
}
// 주어진 값에 대해 -1 하는 부분을 입력과 동시에 map으로 처리
let travel = readLine()!.components(separatedBy: " ").map { Int($0)! - 1 }
let first = disjointSet.find(travel[0])
// 최초 값을 "YES"로 하며 travel 값에 대해 for문을 돌고, first와 다르다면 NO로 변경 후 break
var result = "YES"
for city in travel {
if disjointSet.find(city) != first {
result = "NO"
break
}
}
print(result)
원하는 방 번호 배정된 방 번호
1 1
3 3
4 4
1 2
3 5
1 6
k room_number result
10 [1,3,4,1,3,1] [1,3,4,2,5,6]
import Foundation
struct DisjointSet {
var parent: [Int64: Int64]
init() {
parent = [Int64: Int64]()
}
mutating func find(_ num: Int64) -> Int64 {
if let p = parent[num], p != num {
parent[num] = find(p)
return parent[num]!
} else {
parent[num] = num
return num
}
}
mutating func union(_ num: Int64) -> Int64 {
let root = find(num)
parent[root] = root + 1
return root
}
}
func solution(_ k: Int64, _ room_number: [Int64]) -> [Int64] {
var disjointSet = DisjointSet()
var result: [Int64] = []
for room in room_number {
let nextRoom = disjointSet.union(room)
result.append(nextRoom)
}
return result
}
solution(10, [1,3,4,1,3,1])
제가 학습한 내용을 요약하여 정리한 것입니다. 내용에 오류가 있을 수 있으며, 어떠한 피드백도 감사히 받겠습니다.
감사합니다.