Β - Last update: 2023-05-24

LCAλŠ” Least Common Ancestor의 μ•½μžμ΄λ‹€. Treeμ—μ„œμ˜ 곡톡 쑰상을 μ°ΎλŠ” λ¬Έμ œμ— μ‚¬μš©λ˜λŠ” μ•Œκ³ λ¦¬μ¦˜μ΄λ‹€. 곡톡 쑰상이 μ—¬λŸ¬κ°œ μžˆμ„ 수 μžˆμœΌλ―€λ‘œ, κ·Έ 쀑에 제일 λΉ λ₯Έ 쑰상을 LCA둜 μ •μ˜ν•œλ‹€. inputμœΌλ‘œλŠ” μ„œλ‘œ λ‹€λ₯Έ 두 Nodeκ°€ μ£Όμ–΄μ§„λ‹€. 이 두 Nodeλ₯Ό lκ³Ό r이라 ν•˜μž.

O(n^2)

λ§Œμ•½ Node의 depthλ₯Ό λͺ¨λ₯Έλ‹€λ©΄ 이 방법밖에 사싀 μ‚¬μš©ν•  μˆ˜κ°€ μ—†λ‹€. 각 l에 λŒ€ν•΄(l이 rootκ°€ 될 λ•ŒκΉŒμ§€) λͺ¨λ“  r을 μˆœνšŒν•œλ‹€. μ½”λ“œλ₯Ό 보면 μ•Œκ² μ§€λ§Œ 무쑰건 찾을 μˆ˜λ°–μ— μ—†λ‹€.

for(Node* l = sl; l != 0; l = l->parent) {
for(Node* r = sr; r != 0; r = r->parent) {
if (l == r) return l;
}
}

O(n)

λ§Œμ•½ depthλ₯Ό μ•ˆλ‹€λ©΄, lκ³Ό r의 depthλ₯Ό κ°™κ²Œ 맞좘 이후에, μ–‘μͺ½ Nodeλ₯Ό λ§Œλ‚  λ•ŒκΉŒμ§€ 같이 올리면 λœλ‹€. κ΅¬ν˜„μ˜ 편의λ₯Ό μœ„ν•΄ μ²˜μŒμ—λŠ” 무쑰건 l의 depthκ°€ 크도둝 swap을 ν•˜λŠ” 것이 νŽΈν•˜λ‹€. μ΄λ ‡κ²Œ κ΅¬ν˜„ν•˜λ©΄ 2쀑포문이 μ—†κΈ° λ•Œλ¬Έμ— worstλŠ” O(n)이닀.

if (l->depth < r->depth) swap(l, r);
while(l->depth > r->depth) l = l->parent;
for(;;l = l->parent, r = r->parent) {
if (l == r) return l;
}

O(log n)

O(log n)둜 κ΅¬ν˜„ν•˜λ €λ©΄ μ•„λž˜ 2μ§„μˆ˜μ˜ λ™μž‘λ°©μ‹μ„ 이해할 ν•„μš”κ°€ μžˆλ‹€.

2μ§„μˆ˜μ˜ 힘

λ‹Ήμ—°ν•œ μ†Œλ¦¬μ§€λ§Œ, 2μ§„μˆ˜λŠ” λͺ¨λ“  10μ§„μˆ˜λ₯Ό λ‚˜νƒ€λ‚Ό 수 μžˆλ‹€. λ§Œμ•½μ— μž„μ˜μ˜ Node에 λŒ€ν•΄μ„œ λΆ€λͺ¨λ₯Ό μ μ ˆν•˜κ²Œ μ €μž₯ν•˜λ©΄ λΆ€λͺ¨λ‘œ μ˜¬λΌκ°€λŠ” 과정을 O(n)이 μ•„λ‹Œ O(log n)으둜 ν•  수 μžˆλ‹€. λ°”λ‘œ, λΆ€λͺ¨λ₯Ό μ €μž₯ν•˜λŠ”λ°, 전체 λΆ€λͺ¨λ₯Ό μ €μž₯ν•˜λŠ” 것이 μ•„λ‹ˆλΌ, 1, 2, 4, 8, 처럼 2μ§„μˆ˜μ— μ“°μ΄λŠ” 숫자의 λΆ€λͺ¨λ“€μ„ μ €μž₯ν•˜λŠ” 것이닀. μ΄λ ‡κ²Œ μ €μž₯ν•˜λ©΄, λ§Œμ•½ νŠΉμ • λ…Έλ“œμ˜ 8번째 λΆ€λͺ¨λ₯Ό κ΅¬ν•œλ‹€κ³  ν–ˆμ„ λ•Œ, νŠΉμ •λ…Έλ“œμ˜ 4번째 λΆ€λͺ¨μ˜ 4번째 λΆ€λͺ¨λ₯Ό κ΅¬ν•˜λŠ” μ‹μœΌλ‘œ 생각할 수 있게 λœλ‹€. μ΄λ ‡κ²Œλ§Œ μ €μž₯해도, 7 = 0111(2μ§„μˆ˜λ‘œ) 이기 λ•Œλ¬Έμ—, λͺ¨λ“  μž„μ˜μ˜ μžμ—°μˆ˜μ˜ λΆ€λͺ¨λ₯Ό O(log n) μ‹œκ°„μ— 찾을 수 있게 λœλ‹€. 이λ₯Ό μ‰½κ²Œ κ΅¬ν˜„ν•˜κΈ° μœ„ν•΄μ„œλŠ” bitmaskλ₯Ό μ‚¬μš©ν•˜λ©΄ λœλ‹€. μž„μ˜μ˜ 수만큼의 λΆ€λͺ¨λ‘œ κ°€λŠ” μ½”λ“œλŠ” μ•„λž˜μ™€ κ°™λ‹€.

struct Node {
Node* parents[10]; // μ΅œλŒ€ 2^9의 λΆ€λͺ¨λ₯Ό μ €μž₯ν•œλ‹€.
};
Node* findParent(Node* cur, int num) {
int mask = 0x1;
for(int i = 0; i < 10; ++i, mask <<= 1) {
if (num & mask) cur = cur->parents[i]; // 2의 μ§€μˆ˜μŠΉλ§ŒνΌμ˜ λΆ€λͺ¨λ‘œ μ΄λ™ν•œλ‹€.
}
return cur;
}

2^k의 λΆ€λͺ¨λ₯Ό μ €μž₯ν•˜λŠ” 것은 μ½”λ“œμƒ node->parents[k]λ₯Ό λ³΄λŠ” κ²ƒμœΌλ‘œ κ΅¬ν˜„μ΄ λœλ‹€. 이λ₯Ό ν™œμš©ν•˜λ©΄ LCA도 κ΅¬ν˜„ν•  수 μžˆλ‹€. μœ„ ν…Œν¬λ‹‰μ„ ν™œμš©ν•œλ‹€.

if (l->depth < r->depth) swap(l, r);
int diff = l->depth - r->depth;
// 1. depthλ₯Ό λ§žμΆ°μ€€λ‹€.
int mask = 0x1;
for(int i = 0; i < 10; ++i, mask <<= 1) {
if (num & mask) l = l->parents[i]; // 2의 μ§€μˆ˜μŠΉλ§ŒνΌμ˜ λΆ€λͺ¨λ‘œ μ΄λ™ν•œλ‹€.
}
if (l == r) return l;
// 2. κ°™μ•„μ§€κΈ° μ§μ „κΉŒμ§€ μ˜¬λΌκ°„λ‹€.
for(int i = 10 - 1; i >= 0; --i) {
Node* nl = l->parents[i];
Node* nr = r->parents[i];
if (nl == nr) continue;
l = nl; r = nr;
}
while(l != r) {
l = l->parents[0];
r = r->parents[0];
}
return l;

그런데, μ„€λͺ…쀑에 ν•œκ°€μ§€ 빠뜨린 것이 μžˆλ‹€. parents[]의 μ΄ˆκΈ°ν™”λŠ” μ–΄λ–»κ²Œ ν•  것인가? 이λ₯Ό μ‰½κ²Œ ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ•„λž˜μ™€ 같은 μˆ˜μ‹μ„ ν™œμš©ν•œλ‹€.

2^k = 2^(k-1) + 2^(k-1)

사싀 λ‹Ήμ—°ν•œ μˆ˜μ‹μΈλ°, μƒκ°ν•˜κΈ΄ 사싀 쉽지 μ•Šλ‹€. 이 μˆ˜μ‹μ„ ν™œμš©ν•΄μ„œ parents[]λ₯Ό μ΄ˆκΈ°ν™” ν•  수 μžˆλ‹€.

for(int i = 0; i < 10; ++i) {
if (n->parents[i - 1] != nullptr)
n->parents[i] = n->parents[i - 1]->parents[i - 1];
else n->parents[i] = nullptr;
}

μƒˆλ‘œμš΄ 값이 좔가될 λ•Œ, μœ„μ™€ 같이 Nodeλ₯Ό μ—…λ°μ΄νŠΈ ν•˜λ©΄ λœλ‹€. λ§Œμ•½ Addκ°€ λ‹€ 된 μƒνƒœμ—μ„œ parents[]λ₯Ό μ±„μ›Œμ•Ό ν•œλ‹€λ©΄, root λΆ€ν„° μ‹œμž‘ν•΄μ„œ μžμ‹μœΌλ‘œ μ±„μš°λ©΄ λœλ‹€.

🏷️ 주제 λͺ©λ‘: