테마 선택

📚 지식 노트

AI·하드웨어·주변기기·게임·컴퓨터 시스템·이론 컴퓨터 과학·수학·개발과 운영·보안·응용 분야에 대해 알아 두면 좋은 내용을 정리한 노트다. 위에서 분야를 고르고 아래에서 노트를 고른다.

트리 문제

개념마다 확인(뜻을 맞게 알았나) → 추적(손으로 한 단계씩) → 응용(처음 보는 상황) 순서로 푼다. 풀이 보기를 누르기 전에 종이에 먼저 적어 보자.

목차 (7)
  1. 트리의 낱말
  2. 이진 탐색 트리
  3. AVL·레드-블랙 트리
  4. B-트리
  5. 이진 힙과 우선순위 큐
  6. 트라이
  7. 세그먼트 트리·펜윅 트리

트리의 낱말

확인

아래 트리에서 (1) 잎을 모두 적고, (2) F의 부모와 형제, (3) H의 깊이와 트리의 높이, (4) B를 뿌리로 한 부분 트리의 노드 수를 답하라.

뿌리 A 아래 B, C, D가 있고, B 아래 E, F, D 아래 G, F 아래 H, I, I 아래 J가 있는 트리깊이 0깊이 1깊이 2깊이 3깊이 4ABEFHIJCDG
노드 열 개짜리 일반 트리. 왼쪽 숫자는 층의 깊이다.
풀이 보기

(1) 잎은 C, E, G, H, J 다섯 개. (2) F의 부모는 B, 형제는 E. (3) H의 깊이는 3, 트리 높이는 4. (4) 부분 트리 노드는 6개(B, E, F, H, I, J).

잎은 자식이 없는 노드라 층과 상관없다. C는 깊이 1인데도 잎이다. 높이는 뿌리에서 가장 먼 잎까지의 간선 수라 A → B → F → I → J의 4다. 노드가 10개이니 간선은 언제나 9개(뿌리를 뺀 노드마다 부모로 가는 간선 하나)다.

추적

빈 이진 탐색 트리에 50, 30, 70, 20, 40, 60, 45, 42를 차례로 넣었다. 잎의 높이를 0으로 두고 아래에서 위로 각 노드의 높이를 구하라. 높이가 3인 노드는 무엇이고, 42의 깊이는 얼마인가?

뿌리 50 아래 왼쪽 30, 오른쪽 70이 있고 40 아래로 45, 42가 이어지는 이진 탐색 트리2030404245506070
여덟 노드를 넣은 이진 탐색 트리.
풀이 보기

높이 3인 노드는 30이고, 42의 깊이는 4다. 노드 옆 h가 각 노드의 높이다.

각 노드 옆에 높이를 적은 트리: 50은 4, 30은 3, 40은 2, 45와 70은 1, 나머지 잎은 020h=030h=340h=242h=045h=150h=460h=070h=1
노드마다 높이를 적었다. 부모의 높이는 두 자식 높이 가운데 큰 값 + 1이다.

자식부터 답을 알아야 부모를 계산할 수 있으므로 후위 순회(왼쪽 → 오른쪽 → 자신) 순서다. 빈 자리의 높이를 −1로 두면 잎은 1 + max(−1, −1) = 0으로 자연스럽게 맞는다. 깊이는 반대로 뿌리에서 내려오며 1씩 더한다(50 → 30 → 40 → 45 → 42, 간선 4개).

응용

노드 7개짜리 이진 트리의 간선 수는? 높이가 가장 낮을 때와 가장 높을 때는 각각 얼마인가? 노드가 1,000개라면?

노드 7개로 만든 꽉 찬 이진 트리와 오른쪽으로만 이어진 한 줄 트리꽉 찬 모양1234567한 줄1234567
같은 노드 7개로 만든 두 모양.
풀이 보기

간선 6개, 높이는 가장 낮으면 2, 가장 높으면 6이다. 노드 1,000개면 가장 낮게 9, 가장 높게 999다.

깊이 d 층에는 노드가 많아야 2d개라 높이 h인 이진 트리는 많아야 2h+1 − 1개를 담는다. 7 = 2³ − 1이라 높이 2로 꽉 차고, 1,000개는 2¹⁰ − 1 = 1,023개까지 담는 높이 9가 필요하다(⌊log₂ 1000⌋ = 9). 가장 높은 경우는 층마다 하나뿐인 한 줄로 n − 1이다. 연산 시간이 높이에 비례하므로 이 차이(9 대 999)가 곧 균형 트리가 필요한 이유다.

이진 탐색 트리

확인

세 트리 가운데 이진 탐색 트리(BST)는 어느 것인가? 아닌 것은 규칙을 어디서 어겼는지 말하라.

세 트리: (가) 8 아래 4와 12, 4 아래 2와 6. (나) 4 아래 5와 6. (다) 4 아래 2와 9(가)246812(나)546812(다)249812
노드 다섯 개짜리 트리 셋.
풀이 보기

BST는 (가)뿐이다. (나)는 4의 왼쪽 자식 5가 4보다 커서 부모와 자식만 봐도 틀렸다.

(나)에서는 5가, (다)에서는 9가 규칙을 어겼다(나)546812(다)249812
규칙을 어긴 노드를 빨간 테두리로 표시했다.

(다)가 함정이다. 9는 부모 4보다 크니 4의 오른쪽 자식으로는 맞다. 그러나 9는 8의 왼쪽 부분 트리에 있으므로 8보다 작아야 한다. BST 규칙은 바로 아래 자식만이 아니라 부분 트리 전체에 걸린다. 그래서 검사할 때는 노드마다 "허용 범위(하한, 상한)"를 내려보내며 확인해야 한다. 중위 순회가 2, 4, 9, 8, 12로 정렬이 깨진 것으로도 알 수 있다.

추적

50, 30, 70, 20, 40, 60, 80을 차례로 넣은 BST가 있다. 여기에 45를 넣으면 어느 노드의 어느 쪽 자식이 되는가? 그 뒤 65를 찾으면 비교를 몇 번 하고 결과는 무엇인가?

뿌리 50, 둘째 층 30과 70, 셋째 층 20, 40, 60, 80인 BST20304050607080
일곱 노드의 꽉 찬 BST.
풀이 보기

45는 40의 오른쪽 자식이 된다. 65 찾기는 50, 70, 60과 비교 3번 뒤 빈 자리에 닿아 "없음"이다.

BST 단계 그림: 45를 넣고 65를 찾는다20304050607080203040506070802030405060708020304050607080203040455060708020304045506070802030404550607080203040455060708020304045506070802030404550607080지나온 길지금 비교하는 노드방금 바뀐 노드
1 / 10

시작 트리. 모든 노드에서 왼쪽은 작고 오른쪽은 크다.

45 넣기와 65 찾기. 넣기는 찾기와 똑같이 내려가다 빈 자리에서 멈춘다.

45 > 50? 아니라 왼쪽, 45 > 30이라 오른쪽, 45 > 40이라 오른쪽인데 비어 있어 거기 단다. 찾기 실패도 높이만큼만 비교하므로 O(h)다.

추적

50, 30, 70, 20, 40, 60, 80, 35, 45, 65를 차례로 넣은 BST에서 30을 지우고, 이어서 50을 지운다(자식이 둘이면 후계자로 바꿔 끼운다). 두 번 지운 뒤의 뿌리와 중위 순회 결과는?

뿌리 50 아래 30과 70, 30 아래 20과 40, 40 아래 35와 45, 70 아래 60과 80, 60 아래 65인 BST20303540455060657080
노드 열 개짜리 BST.
풀이 보기

뿌리는 60이고, 중위 순회는 20, 35, 40, 45, 60, 65, 70, 80이다.

BST 단계 그림: 30을 후계자 35로, 50을 후계자 60으로 바꿔 지운다2030354045506065708020303540455060657080203035404550606570802030354045506065708020354045506065708020354045506065708020354045506065708020354045606570802035404560657080지나온 길지금 비교하는 노드방금 바뀐 노드
1 / 9

시작 트리. 모든 노드에서 왼쪽은 작고 오른쪽은 크다.

자식이 둘인 노드 두 번 지우기. 후계자는 오른쪽 부분 트리에서 왼쪽 끝까지 내려가 찾는다.

30의 후계자는 오른쪽 부분 트리(40) 왼쪽 끝의 35다. 35는 잎이라 그냥 떼어 낸다. 50의 후계자는 70 → 60에서 멈춘 60인데, 60에는 오른쪽 자식 65가 있어 그 자리를 65가 메운다. 후계자에게 왼쪽 자식이 없다는 사실 덕분에 두 경우 모두 쉬운 지우기로 끝난다.

응용

1부터 7까지를 BST에 넣는다. 작은 순서로 넣으면 높이가 얼마인가? 높이가 가장 낮아지는 넣는 순서를 하나 들어 보라. 7! = 5,040가지 순서 가운데 한 줄 모양(높이 6)이 되는 순서는 몇 가지인가?

정렬된 순서로 넣은 한 줄 트리와 가운데부터 넣은 꽉 찬 트리1, 2, …, 7 순서12345674, 2, 6, 1, 3, 5, 7 순서1234567
같은 일곱 값도 넣는 순서에 따라 모양이 이렇게 달라진다.
풀이 보기

작은 순서로 넣으면 높이 6(한 줄). 4, 2, 6, 1, 3, 5, 7처럼 가운데부터 넣으면 높이 2. 한 줄이 되는 순서는 64가지다.

한 줄이 되려면 매번 남은 값 가운데 가장 작거나 가장 큰 값을 넣어야 한다. 마지막 하나를 빼고 여섯 번 둘 중 하나를 고르므로 2⁶ = 64가지다. 5,040가지를 모두 돌려 보면 높이 2(꽉 찬 모양)는 80가지, 높이 3이 2,240가지로 가장 많다. 무작위 순서면 대개 낮지만, 이미 정렬된 데이터가 들어오는 흔한 경우가 하필 최악이라는 것이 BST의 약점이다.

AVL·레드-블랙 트리

확인

AVL 트리에 세 값을 넣었더니 뿌리의 균형이 깨진 네 모양이 나왔다. 각각 LL·LR·RR·RL 가운데 어느 경우이고, 어떤 회전으로 고치는가?

세 노드로 된 네 모양: 왼쪽으로 곧은 줄, 왼쪽으로 꺾인 줄, 오른쪽으로 곧은 줄, 오른쪽으로 꺾인 줄(가)102030(나)102030(다)102030(라)102030
빨간 테두리가 균형 인수 ±2가 된 노드다.
풀이 보기

(가) LL: 30에서 오른쪽 회전. (나) LR: 10에서 왼쪽 회전 뒤 30에서 오른쪽 회전. (다) RR: 10에서 왼쪽 회전. (라) RL: 30에서 오른쪽 회전 뒤 10에서 왼쪽 회전.

곧은 줄은 회전 한 번, 꺾인 줄은 먼저 아래 자식에서 돌려 곧은 줄로 편 뒤 한 번 더 돈다. 네 경우 모두 결과는 가운데 값 20이 뿌리, 10과 30이 양쪽 자식인 같은 모양이다. 아래는 (라) RL 경우의 두 번 회전이다.

AVL 단계 그림: 10, 30, 20을 넣어 RL 경우를 두 번 회전으로 고친다10010-130010-220030+110-220-1300100200300방금 넣은 노드·회전으로 올라간 노드균형이 깨진 노드+n왼쪽이 n만큼 높다
1 / 5

10 넣음. 모든 노드의 좌우 높이 차가 1 이하라 그대로 둔다.

RL 경우. 먼저 30에서 오른쪽 회전해 RR 모양으로 펴고, 10에서 왼쪽 회전한다.

확인

아래 트리를 뿌리 40에서 오른쪽으로 회전하면 어떤 모양이 되는가? 부모가 바뀌는 부분 트리는 무엇이고, 중위 순회 결과는 바뀌는가?

뿌리 40 아래 왼쪽 20, 오른쪽 50, 20 아래 10과 30인 트리1020304050
회전하기 전. 40의 왼쪽 자식 20이 위로 올라올 차례다.
풀이 보기

20이 뿌리가 되고 40은 20의 오른쪽 자식으로 내려간다. 30이 20의 오른쪽에서 40의 왼쪽으로 옮겨 가고, 중위 순회는 10, 20, 30, 40, 50 그대로다.

회전 전 40이 뿌리인 트리와 회전 후 20이 뿌리이고 30이 40의 왼쪽 자식이 된 트리회전 전102030405040에서 오른쪽 회전 후1020304050
회전 전과 후. 칠한 30만 부모가 바뀌었다.

30은 20보다 크고 40보다 작으니 "20의 오른쪽"이든 "40의 왼쪽"이든 크기 순서상 같은 자리다. 바뀌는 포인터는 20.right, 40.left, 그리고 부모가 가리키던 뿌리 셋뿐이라 O(1)이고, 순서를 깨지 않으므로 BST 규칙도 그대로다.

추적

빈 AVL 트리에 10, 20, 30, 25, 28, 27, 5를 차례로 넣는다. 회전이 일어나는 때마다 어느 노드에서 어떤 경우(LL·LR·RR·RL)인지 적고, 마지막 트리의 뿌리를 답하라.

AVL에 넣을 값: 10, 20, 30, 25, 28, 27, 5넣는 순서1020302528275
넣을 순서.
풀이 보기

30을 넣을 때 10에서 RR, 28을 넣을 때 30에서 LR, 27을 넣을 때 20에서 RL, 5를 넣을 때 20에서 LL. 마지막 뿌리는 25다.

AVL 단계 그림: 일곱 값을 넣으며 RR, LR, RL, LL 네 경우의 회전이 한 번씩 일어난다10010-120010-220-130010020030010020-125030+110020-225-128030+210020-225028+130+210020-125028030010020-225-127028+130010020-225-227028030010020+12502702803005010+120+225+127028030050100200250270280300방금 넣은 노드·회전으로 올라간 노드균형이 깨진 노드+n왼쪽이 n만큼 높다
1 / 13

10 넣음. 모든 노드의 좌우 높이 차가 1 이하라 그대로 둔다.

네 가지 경우가 한 번씩 모두 나온다. 노드 옆 숫자는 균형 인수다.

넣은 값에서 뿌리 쪽으로 올라가며 처음 만나는 ±2 노드만 고치면 된다. 마지막 트리는 25 아래 10(5, 20)과 28(27, 30)로 높이 2다. 같은 값을 그냥 BST에 넣었다면 10 → 20 → 30 → 25 → 28 → 27로 높이 5가 됐다.

응용

열쇠 100만 개를 담는다. 가장 낮은 이진 트리, AVL 트리(높이 ≤ 1.44·log₂ n), 레드-블랙 트리(높이 ≤ 2·log₂(n+1)), 정렬된 순서로 넣은 그냥 BST의 높이를 각각 어림하라. 찾기 한 번의 비교 횟수로 보면 어떤 차이인가?

높이 비교 막대: 가장 낮은 이진 트리 19, AVL 상한 28, 레드-블랙 상한 39, 한 줄 BST 999,999가장 낮은 트리19AVL 상한28레드-블랙 상한39한 줄 BST999,999 (막대가 그림 밖으로 2만 배 넘게 길다)막대 길이: 높이 1당 7px
n = 1,000,000일 때 높이(간선 수)의 상한. 한 줄 BST는 축을 크게 벗어난다.
풀이 보기

가장 낮으면 19, AVL은 28 이하, 레드-블랙은 39 이하, 한 줄 BST는 999,999다. log₂ 1,000,000 ≈ 19.93이라 ⌊19.93⌋ = 19, 1.44 × 19.93 ≈ 28.7, 2 × log₂ 1,000,001 ≈ 39.9다.

찾기 비교는 높이 + 1번 이하이므로 균형 트리는 최악에도 20~40번, 한 줄 BST는 최악 100만 번이다. AVL과 레드-블랙의 차이(28 대 39)는 한 자릿수 배수도 안 되지만, 균형이 없을 때와의 차이는 수만 배다. 그래서 "어떤 균형 트리냐"보다 "균형이 있느냐"가 훨씬 중요하다.

B-트리

확인

노드마다 열쇠를 2개까지 담는 B-트리(2-3 트리)다. 55를 찾을 때 거치는 노드를 차례로 적고, 디스크에서 노드를 몇 번 읽는지 답하라. 열쇠가 2개인 노드는 자식이 몇 개인가?

뿌리 40 아래 20과 60, 그 아래 잎 10, 30, 50, 70인 B-트리40201030605070
10부터 70까지 일곱 열쇠를 넣은 2-3 트리.
풀이 보기

[40] → [60] → [50]을 거쳐 3번 읽고, 잎에도 55가 없어 "없음"이다. 열쇠 2개인 노드는 자식이 3개다.

55를 찾으며 40, 60, 50 노드를 차례로 읽는다40201030605070
55 찾기에서 읽는 노드를 칠했다. 층마다 하나씩이다.

55 > 40이라 40 오른쪽 틈의 자식, 55 < 60이라 60 왼쪽 틈의 자식으로 간다. 열쇠 k개가 수직선을 k+1개 구간으로 나누고 구간마다 자식이 하나씩이라 자식은 k+1개다. 모든 잎이 같은 깊이에 있으니 어느 열쇠를 찾든 읽기 횟수는 층 수와 같다.

추적

빈 2-3 트리(노드당 열쇠 최대 2개)에 50, 20, 80, 10, 30, 60, 90, 40, 70을 차례로 넣는다. 노드가 갈라지는 일은 모두 몇 번 일어나고, 마지막 트리는 몇 층인가?

B-트리에 넣을 값: 50, 20, 80, 10, 30, 60, 90, 40, 70넣는 순서502080103060904070
넣을 순서.
풀이 보기

갈라지기는 4번(80, 30을 넣을 때 한 번씩, 90을 넣을 때 잎과 뿌리 연달아 두 번), 마지막 트리는 3층이다. 뿌리 [50], 둘째 층 [20]·[80], 잎 [10]·[30, 40]·[60, 70]·[90]이다.

B-트리 단계 그림: 아홉 열쇠를 넣으며 네 번 갈라지고 뿌리가 두 번 새로 생긴다50205020508050208050102080501020308020501030802050103060802050103060809020508010306090502010308060905020103040806090502010304080607090방금 넣거나 올라간 열쇠열쇠가 넘친 노드선: 열쇠 사이 틈 → 그 범위의 자식
1 / 13

50 넣음. 잎에 빈칸이 있어 그대로 들어간다.

90을 넣으면 잎이 갈라지며 80이 올라가고, 그 바람에 뿌리도 넘쳐 다시 갈라진다.

뿌리가 갈라질 때만 층이 늘어(80과 90을 넣을 때) 3층이 됐다. 마지막 40과 70은 빈칸이 있는 잎에 그냥 들어간다. 갈라짐이 위로 번지는 일은 드물어, 넣기 한 번의 평균 비용은 거의 잎 하나 고치기다.

응용

노드 하나가 디스크 블록 하나이고 자식을 최대 200개(열쇠 최대 199개) 다는 B-트리에 열쇠 10억 개를 담는다. 노드를 꽉 채우면 몇 층이면 되는가? 노드가 최소한(자식 100개)만 찼다면? 균형 이진 트리라면?

층 수 비교 막대: 균형 이진 트리 30층, B-트리 4~5층균형 이진 트리30층B-트리 (m = 200)4~5층막대 길이: 층 하나당 10px
열쇠 10억 개를 찾을 때 읽는 층 수. 층 하나가 디스크 읽기 한 번이다.
풀이 보기

꽉 차면 4층, 최소로 차도 5층이면 충분하다. 균형 이진 트리는 30층이 필요하다.

꽉 찬 L층 트리는 열쇠를 200L − 1개 담는다. 3층은 약 800만 개라 모자라고 4층은 약 16억 개라 넉넉하다. 최소로 찬 경우 뿌리는 자식 2개, 나머지는 100개씩이라 L층이면 적어도 2 × 100L−1 − 1개가 들어간다. 5층의 최소가 약 2억, 6층의 최소가 약 200억이니 10억 개는 5층을 넘을 수 없다. 이진 트리는 ⌈log₂(10억 + 1)⌉ = 30층이다. 디스크 읽기 30번과 4~5번(윗층은 캐시에 있어 실제로는 1~2번)의 차이가 데이터베이스 색인이 B-트리인 이유다.

이진 힙과 우선순위 큐

확인

세 배열 가운데 최소 힙인 것은? 그리고 (가)에서 1번 칸의 자식은 몇 번 칸이고, 5번 칸의 부모는 몇 번 칸인가?

세 배열: (가) 1, 3, 2, 7, 4, 5. (나) 2, 4, 3, 8, 1, 9. (다) 1, 2, 3, 4, 5, 6(가)103122734455(나)204132831495(다)102132435465
배열 세 개. 칸 아래 숫자는 위치(0부터)다.
풀이 보기

(가)와 (다)가 최소 힙이고 (나)는 아니다. (가)에서 1번 칸의 자식은 3·4번 칸(7과 4), 5번 칸의 부모는 2번 칸(2)이다.

배열 2, 4, 3, 8, 1, 9를 층 순서로 놓은 트리에서 4 아래의 1이 규칙을 어긴다841293
(나)를 트리로 그린 모양. 4번 칸의 1이 부모 4보다 작아 힙 조건을 어겼다.

i번 칸의 자식은 2i+1·2i+2번, 부모는 ⌊(i−1)/2⌋번이다. (나)의 4번 칸 1의 부모는 ⌊3/2⌋ = 1번 칸의 4라 조건이 깨졌다. (다)처럼 오름차순으로 정렬된 배열은 늘 최소 힙이지만, 거꾸로 힙이라고 정렬돼 있지는 않다((가)의 3과 2처럼 형제 사이에는 순서가 없다).

추적

빈 최소 힙에 6, 2, 8, 1, 7, 3을 차례로 넣은 뒤 두 번 꺼낸다. 여섯 개를 넣은 직후의 배열, 두 번 꺼낸 뒤의 배열을 적고, 그동안 두 칸을 바꾸는 일이 모두 몇 번 일어나는지 세어 보라.

연산 순서: 6, 2, 8, 1, 7, 3 넣기 뒤 꺼내기 두 번연산628173꺼냄꺼냄
연산 순서. 꺼내기는 늘 가장 작은 값을 내놓는다.
풀이 보기

넣은 직후 [1, 2, 3, 6, 7, 8], 두 번 꺼낸 뒤(1과 2가 나옴) [3, 6, 7, 8]이고, 바꾸기는 모두 7번이다.

최소 힙 단계 그림: 여섯 값을 넣고 두 번 꺼낸다6666222266226688226688226688112211886611228866112288667711228866771122886677331122336677881122336677888822336677꺼낸 값 12288336677꺼낸 값 12266338877꺼낸 값 12266338877꺼낸 값 177663388꺼낸 값 233667788꺼낸 값 233667788꺼낸 값 2012345배열비교하는 칸방금 자리를 바꾼 두 칸
1 / 20

6 넣기: 배열 끝(맨 아래 층의 다음 자리)에 붙인다.

넣을 때는 올리기(2, 1이 각각 올라감), 꺼낼 때는 끝 값을 뿌리에 옮겨 내리기.

넣기 쪽 바꾸기가 4번(2가 한 번, 1이 두 번, 3이 한 번 올라감), 꺼내기 쪽이 3번(8이 두 층, 7이 한 층 내려감)이다. 어느 연산도 높이(여기서는 2)보다 많이 바꾸지 않는다. 그것이 O(log n)의 뜻이다.

응용

값이 5, 12, 3, 8, 15, 7, 10 순으로 하나씩 흘러 들어온다. 크기 3인 최소 힙만 써서 "지금까지 가장 큰 값 3개"를 유지하려 한다. 새 값이 힙의 뿌리보다 크면 뿌리를 꺼내고 새 값을 넣는다. 값마다 힙 배열이 어떻게 바뀌는지 적고, 왜 최대 힙이 아니라 최소 힙인지 설명하라.

흘러 들어오는 값: 5, 12, 3, 8, 15, 7, 10입력5123815710
흘러 들어오는 값.
풀이 보기

마지막 힙은 [10, 15, 12]이고, 가장 큰 3개는 10, 12, 15다. 뿌리가 "3등"이라 새 값과 한 번만 비교하면 들일지 버릴지 정해지므로 최소 힙을 쓴다.

값마다 힙 배열: 5 → [5], 12 → [5, 12], 3 → [3, 12, 5], 8 → [5, 12, 8], 15 → [8, 12, 15], 7 → 그대로, 10 → [10, 15, 12]5 들어옴512 들어옴5123 들어옴31258: 3 꺼냄512815: 5 꺼냄812157: 버림8121510: 8 꺼냄101512
값이 들어올 때마다의 힙 배열. 칠한 칸이 뿌리(지금의 3등)다.

7은 3등(8)보다 작아 들어올 자격이 없으니 비교 한 번으로 버린다. 힙 크기가 k로 고정이라 값 하나에 O(log k), 전체 n개에 O(n log k)다. 모든 값을 정렬하는 O(n log n)보다 싸고, 메모리도 k칸이면 된다. 로그에서 "가장 느린 요청 상위 100개"를 고르는 일이 이렇다.

트라이

확인

to, tea, ted, ten, in, inn을 넣은 트라이다. 노드는 뿌리를 포함해 몇 개인가? "te"와 "in"은 저장된 낱말인가? "te"로 시작하는 낱말은?

낱말 여섯 개의 트라이: 뿌리 아래 i와 t 두 갈래, t 아래 e와 o, e 아래 a, d, n, i 아래 n, n·ininninnteateadtedntenoto
채운 상자가 낱말이 끝나는 자리이고, 옆에 그 낱말을 적었다.
풀이 보기

노드는 10개다. "te"는 낱말이 아니고 "in"은 낱말이다. "te"로 시작하는 낱말은 tea, ted, ten이다.

"te" 노드는 길 위에 있지만 끝 표시가 없다. 반대로 "in"은 자식(inn)이 있어도 끝 표시가 있어 낱말이다. 그래서 트라이 노드에는 자식 표 말고도 끝 표시 하나가 꼭 필요하다. 글자 수는 모두 2 + 3 × 3 + 2 + 3 = 16개인데 노드는 뿌리 빼고 9개뿐이다. 앞부분을 함께 쓰기 때문이다.

추적

빈 트라이에 ten, tea, to, tent, in, t를 차례로 넣는다. 낱말마다 새로 만드는 노드 수를 적고, 마지막에 접두사 "te"로 찾은 결과를 답하라.

넣을 낱말: ten, tea, to, tent, in, t넣는 순서tenteatotentint
넣을 낱말과 순서.
풀이 보기

새 노드는 차례로 3, 1, 1, 1, 2, 0개(뿌리까지 모두 9개)이고, "te" 검색 결과는 tea, ten, tent다.

트라이 단계 그림: 여섯 낱말을 넣고 te로 시작하는 tea, ten, tent를 찾는다·tenten·teateanten·teateantenoto·teateantenttentoto·ininteateantenttentoto·inintteateantenttentoto·inintteateantenttentoto·inintteateantenttentoto·inintteateantenttentoto찾은 낱말: tea, ten, tent낱말 끝지나는 길새로 만든 노드
1 / 9

"ten" 넣기: 새 노드 3개를 만든다.

새로 만든 노드는 빨간 테두리. 마지막 t는 길이 이미 있어 끝 표시만 단다.

새 노드 수는 "이미 있는 낱말과 겹치지 않는 뒷부분의 길이"다. tent는 ten 길을 3글자 함께 쓰고 t 하나만 더한다. 마지막 t는 노드를 하나도 만들지 않고 이미 있던 t 노드에 끝 표시만 단다.

응용

test, team, tea, toast, to를 담은 트라이를 압축 트라이(radix tree)로 바꾼다. 갈래도 낱말 끝도 없는 외길을 한 간선으로 합칠 때, 노드는 몇 개에서 몇 개로 줄어드는가(뿌리 포함)?

낱말 다섯 개의 트라이: t 아래 e와 o로 갈라지고, e 아래 a와 s, o 아래 a, s, t가 한 줄로 이어진다·teateamteamsttestotoasttoast
압축하기 전의 트라이.
풀이 보기

11개에서 8개로 준다. te 아래 s → t가 "st" 하나로, to 아래 a → s → t가 "ast" 하나로 합쳐진다.

압축 트라이: t 아래 e와 o, e 아래 a와 st, a 아래 m, o 아래 ast·teateamteamsttestotoasttoast
압축 트라이. 상자 하나가 글자 묶음 하나를 맡는다.

t와 e는 자식이 하나지만 합칠 수 없다. t 아래는 e와 o 두 갈래이고, e도 a와 s 두 갈래이기 때문이다. a는 자식이 m 하나뿐이지만 "tea"의 끝이라 합치면 tea를 잃는다. 노드가 줄면 포인터와 자식 표도 줄어 메모리가 크게 준다. 라우팅 표나 키-값 저장소의 색인이 이 압축 트라이를 쓰는 까닭이다.

세그먼트 트리·펜윅 트리

확인

배열 [3, 1, 4, 1, 5, 9, 2, 6]의 구간 합 세그먼트 트리다. 뿌리와 "4–7" 노드에는 어떤 값이 있는가? 5번 칸(0부터 셈)의 9를 4로 바꾸면 고쳐야 하는 노드는 무엇이고, 뿌리는 얼마가 되는가?

배열 3, 1, 4, 1, 5, 9, 2, 6의 세그먼트 트리: 뿌리 31, 둘째 층 9와 22310–790–3224–740–152–3144–586–730번11번42번13번54번95번26번67번
노드 위 숫자는 합, 아래는 맡은 구간이다. 칸 번호는 0부터다.
풀이 보기

뿌리는 31, "4–7" 노드는 22다. 고칠 노드는 5번 잎 → 4–5 → 4–7 → 0–7의 네 개이고, 뿌리는 26이 된다.

5번 잎, 4–5, 4–7, 뿌리 네 노드를 칠한 세그먼트 트리310–790–3224–740–152–3144–586–730번11번42번13번54번95번26번67번
5번 칸을 바꿀 때 고치는 노드. 잎에서 뿌리까지 한 길이다.

5번 칸을 구간에 품은 노드만 합이 바뀌고, 그런 노드는 층마다 하나씩이다. 모두 −5씩 고쳐 22 → 17, 31 → 26이 된다. 층 수가 log₂ n + 1이니 갱신은 O(log n)이다.

추적

같은 배열 [3, 1, 4, 1, 5, 9, 2, 6]에서 1번부터 5번 칸까지의 합을 세그먼트 트리로 구한다. 통째로 답에 쓰는 노드는 어느 것이고, 답은 얼마인가? 모두 몇 개의 노드를 들여다보는가?

배열 3, 1, 4, 1, 5, 9, 2, 6에서 1번부터 5번 칸을 칠했다배열3011421354952667
구할 구간. 칸 아래 숫자는 위치다.
풀이 보기

통째로 쓰는 노드는 [1, 1]·[2, 3]·[4, 5] 세 개이고 답은 1 + 5 + 14 = 20이다. 들여다보는 노드는 9개다.

세그먼트 트리 단계 그림: 1번부터 5번 칸 합 20을 노드 세 개로 구한다310–790–3224–740–152–3144–586–730번11번42번13번54번95번26번67번누계 0310–790–3224–740–152–3144–586–730번11번42번13번54번95번26번67번누계 0310–790–3224–740–152–3144–586–730번11번42번13번54번95번26번67번누계 0310–790–3224–740–152–3144–586–730번11번42번13번54번95번26번67번누계 0310–790–3224–740–152–3144–586–730번11번42번13번54번95번26번67번누계 0310–790–3224–740–152–3144–586–730번11번42번13번54번95번26번67번누계 1310–790–3224–740–152–3144–586–730번11번42번13번54번95번26번67번누계 6310–790–3224–740–152–3144–586–730번11번42번13번54번95번26번67번누계 6310–790–3224–740–152–3144–586–730번11번42번13번54번95번26번67번누계 20310–790–3224–740–152–3144–586–730번11번42번13번54번95번26번67번누계 20310–790–3224–740–152–3144–586–730번11번42번13번54번95번26번67번누계 20구하는 구간 [1, 5]통째로 씀걸쳐서 내려감흐린 글씨: 구간 밖이라 버림지금 보는 노드
1 / 11

[1, 5] 구간 합 구하기. 뿌리부터 본다.

[1, 5] 구간 합. 걸치는 노드만 내려가고, 밖의 [0, 0]과 [6, 7]은 바로 버린다.

칸 다섯 개를 하나씩 더하는 대신 미리 더해 둔 노드 세 개를 더했다. 배열이 길어져도 걸치는 노드는 층마다 많아야 둘이라, 들여다보는 노드 수는 O(log n)에 머문다.

응용

같은 배열을 펜윅 트리로 담는다(칸 번호는 1부터). t[1]~t[8]을 적고, 앞 6칸 합을 구할 때 더하는 칸, 3번부터 7번 칸까지의 합, 그리고 3번 칸의 값을 바꿀 때 고쳐야 하는 칸을 답하라.

1부터 센 칸 번호 1에서 8과 값 3, 1, 4, 1, 5, 9, 2, 6칸 번호12345678값31415926
펜윅 트리는 칸 번호를 1부터 센다.
풀이 보기

t = [3, 4, 4, 9, 5, 14, 2, 31]. 앞 6칸 합은 t[6] + t[4] = 14 + 9 = 23. 3~7번 합은 앞 7칸 합 25 − 앞 2칸 합 4 = 21. 3번 칸을 바꾸면 t[3], t[4], t[8]을 고친다.

펜윅 트리 그림: 앞 6칸 합은 t[6]과 t[4]를 더하고, 3번 값을 바꾸면 t[3], t[4], t[8]을 고친다앞 6칸 합 = t[6] + t[4] = 14 + 9 = 23t[1]=3t[2]=4t[3]=4t[4]=9t[5]=5t[6]=14t[7]=2t[8]=313112431455962768원래 배열길이 8길이 4길이 2길이 1앞 6칸 합에 쓰는 칸: i에서 lowbit(i)를 빼며 내려간다(6 → 4)3번 값을 바꿀 때 고치는 칸: i에 lowbit(i)를 더하며 올라간다(3 → 4 → 8)
칸마다 맡는 구간. 칠한 칸이 앞 6칸 합에 쓰는 칸, 빨간 테두리가 3번 값을 바꿀 때 고치는 칸이다.

6 = 110₂에서 가장 낮은 1비트(2)를 빼면 4 = 100₂, 또 빼면 0이라 두 칸이면 끝난다. 갱신은 거꾸로 3 = 011₂에 lowbit 1을 더해 4, 4에 4를 더해 8로 올라간다. 펜윅 트리는 "앞에서부터의 합"만 직접 주므로 구간 합은 두 앞부분 합의 차로 구한다. 그래서 빼기가 되는 연산(합)에는 쓰지만, 빼기가 없는 최솟값에는 세그먼트 트리를 쓴다.