Skip to content

Commit 9b3787e

Browse files
committed
docs: 세그먼트 트리 문서 업데이트
1 parent eee43b2 commit 9b3787e

File tree

2 files changed

+59
-29
lines changed

2 files changed

+59
-29
lines changed

_posts/2024-04-28-tree.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ Algorithm</p></blockquote>
9797

9898
저장된 자료들을 적절히 전처리해 그들에 대한 질의들을 빠르게 대답할 수 있게 구현한 트리입니다. 주로 구간 합, 구간 최솟값, 구간 최댓값 등 1차원 배열의 특정 구간에 대한 질문을 빠르게 대답하는 데 사용됩니다.
9999

100+
세그먼트 트리에 대한 내용은 다음 링크에 작성하였습니다.
101+
102+
<a href="../segment-tree">세그먼트 트리 (Segment Tree)</a>
103+
100104
- `힙(Heap)`
101105

102106
부모 노드의 값이 자식 노드의 값보다 항상 크거나(최대 힙), 작은(최소 힙) 완전 이진 트리입니다. 주로 우선순위 큐를 구현하거나, 힙 정렬에 사용됩니다.

_posts/2024-05-21-segment-tree.md

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Algorithm</p></blockquote>
2323

2424
### 개념
2525

26-
`세그먼트 트리(Segment Tree)`는 저장된 자료들을 적절히 전처리해 그들에 대한 질의들을 빠르게 대답할 수 있게 구현한 이진 트리 기반 자료 구조입니다. 즉, 세그머트 트리는 배열의 구간 정보를 트리 형태로 저장하는 자료 구조로, 주로 구간 합, 구간 최솟값, 구간 최댓값 등 1차원 배열의 특정 구간에 대한 질문을 빠르게 대답하는 데 사용됩니다.
26+
`세그먼트 트리(Segment Tree)`는 저장된 자료들을 적절히 전처리해 그들에 대한 질의들을 빠르게 대답할 수 있게 구현한 이진 트리 기반 자료 구조입니다. 즉, 세그먼트 트리는 배열의 구간 정보를 트리 형태로 저장하는 자료 구조로, 주로 구간 합, 구간 최솟값, 구간 최댓값 등 1차원 배열의 특정 구간에 대한 질문을 빠르게 대답하는 데 사용됩니다.
2727

2828
### 특징
2929

@@ -69,25 +69,32 @@ Algorithm</p></blockquote>
6969

7070
위의 트리 구조에서 루트 노드는 전체 구간에 대한 정보를 저장하고, 왼쪽 자식은 구간의 왼쪽 절반 정보를, 오른쪽 자식은 구간의 오른쪽 절반 정보를 저장합니다.
7171

72-
### 구현
72+
### 구간 합을 처리하는 세그먼트 트리 구현
7373

74-
이번 글에서는 구간 합을 처리하는 세그먼트 트리를 구현하는 방법에 대해서 설명하겠습니다.
74+
이번 글에서는 <b>구간 합을 처리하는 세그먼트 트리</b>를 구현하는 방법에 대해서 설명하겠습니다.
7575

7676
#### 전처리: O(N)
7777

78+
먼저 배열 arr이 주어질 때 세그먼트 트리를 나타낼 배열(tree) 하나를 선언합니다. 세그먼트 트리는 `완전 이진 트리` 형태로 구성되기 때문에 리프의 개수가 N 개 일 때, 전체 노드는 대략 2N ~ 4N 개가 필요합니다. 따라서 인덱스 초과를 방지하기 위해 tree 배열을 초기화할 때 배열의 길이를 `arr.length * 4`로 설정합니다.
79+
7880
```javascript
7981
const arr = [1, 3, 5, 7, 9, 11];
8082
const tree = Array(arr.length * 4);
83+
```
84+
85+
트리 선언 이후에는 다음과 같이 재귀 방식을 활용하여 세그먼트 트리를 생성할 수 있습니다. 루트 노드는 전체 구간에 대한 정보를 저장하고, 왼쪽 자식은 구간의 왼쪽 절반 정보를, 오른쪽 자식은 구간의 오른쪽 절반 정보를 저장하므로 `node`가 루트일 때, 왼쪽 구간 정보와 오른쪽 구간 정보를 활용하여 부모 노드에 값을 저장합니다.
8186

87+
```javascript
8288
/**
8389
* node 노드가 arr[left...right] 배열을 표현할 때
8490
* node를 루트로 하는 서브 트리를 초기화하는 함수
8591
*
86-
* @param {number} left 시작 인덱스
87-
* @param {number} right 끝 인덱스
88-
* @param {number} node 루트
92+
* @param {number} left 현재 노드가 표현하는 구간의 시작 인덱스
93+
* @param {number} right 현재 노드가 표현하는 구간의 끝 인덱스
94+
* @param {number} node 루트 (현재 구간을 표현하는 트리의 노드 번호)
8995
*/
9096
const init = (left, right, node) => {
97+
// 세그먼트 트리의 리프 노드인 경우
9198
if (left === right) {
9299
tree[node] = arr[left];
93100
return;
@@ -100,18 +107,32 @@ const init = (left, right, node) => {
100107
};
101108
```
102109

110+
실제로 세그먼트 트리를 초기화하면 트리 구조는 다음과 같이 구성됩니다.
111+
112+
```text
113+
tree[1] = 36 (전체 합)
114+
/ \
115+
tree[2] = 9 tree[3] = 27
116+
/ \ / \
117+
tree[4]=4 tree[5]=5 tree[6]=16 tree[7]=11
118+
/ \
119+
tree[8]=1 tree[9]=3
120+
```
121+
103122
#### 질의 (Query): O(log N)
104123

124+
다음으로 `arr[nodeLeft...nodeRight]`에 대한 구간 합 연산 결과를 반환하는 query 함수를 구현합니다. 탐색 중인 구간 `[left, right]``[nodeLeft, nodeRight]` 구간에 완전히 포함되는 경우 그 노드의 값을 반환하면 됩니다. 반대로 구간이 겹치지 않는 경우 0를 리턴하여 무시하도록 구현하며, 일부만 겹치는 경우 왼쪽 자식과 오른쪽 자식을 탐색하여 구간 합을 계산하면 됩니다.
125+
105126
```javascript
106127
/**
107128
* node가 표현하는 범위 arr[nodeLeft...nodeRight]가 주어질 때,
108129
* 이 범위와 arr[left...right]의 교집합의 구간 합을 구합니다.
109130
*
110-
* @param {number} left
111-
* @param {number} right
112-
* @param {number} node
113-
* @param {number} nodeLeft
114-
* @param {number} nodeRight
131+
* @param {number} left 구하고자 하는 구간 합의 시작 인덱스
132+
* @param {number} right 구하고자 하는 구간 합의 끝 인덱스
133+
* @param {number} node 현재 탐색 중인 세그먼트 트리의 노드 번호
134+
* @param {number} nodeLeft 현재 노드가 표현하는 구간의 시작 인덱스
135+
* @param {number} nodeRight 현재 노드가 표현하는 구간의 끝 인덱스
115136
* @returns 구간 합
116137
*/
117138
const query = (left, right, node, nodeLeft, nodeRight) => {
@@ -135,16 +156,18 @@ const query = (left, right, node, nodeLeft, nodeRight) => {
135156

136157
#### 갱신 (Update): O(log N)
137158

159+
마지막으로 배열의 값이 변경되었을 때, 해당 구간을 포함하는 노드들을 갱신하는 update 함수를 구현합니다. `arr[index]`의 값이 `newValue`로 바뀌었을 때 세그먼트 트리의 관련된 노드들을 모두 재계산하여 반영합니다.
160+
138161
```javascript
139162
/**
140163
* arr[index] = newValue로 바뀌었을 때
141164
* node를 루트로 하는 세그먼트 트리를 갱신합니다.
142165
*
143-
* @param {number} index
144-
* @param {number} newValue
145-
* @param {number} node
146-
* @param {number} nodeLeft
147-
* @param {number} nodeRight
166+
* @param {number} index 값이 바뀐 배열의 인덱스
167+
* @param {number} newValue 새로운 값
168+
* @param {number} node 현재 탐색 중인 세그먼트 트리의 노드 번호
169+
* @param {number} nodeLeft 현재 노드가 표현하는 구간의 시작 인덱스
170+
* @param {number} nodeRight 현재 노드가 표현하는 구간의 끝 인덱스
148171
*/
149172
const update = (index, newValue, node, nodeLeft, nodeRight) => {
150173
// index가 노드가 표현하는 구간과 상관없는 경우에는 무시합니다.
@@ -177,11 +200,12 @@ const tree = Array(arr.length * 4);
177200
* node 노드가 arr[left...right] 배열을 표현할 때
178201
* node를 루트로 하는 서브 트리를 초기화하는 함수
179202
*
180-
* @param {number} left 시작 인덱스
181-
* @param {number} right 끝 인덱스
182-
* @param {number} node 루트
203+
* @param {number} left 현재 노드가 표현하는 구간의 시작 인덱스
204+
* @param {number} right 현재 노드가 표현하는 구간의 끝 인덱스
205+
* @param {number} node 루트 (현재 구간을 표현하는 트리의 노드 번호)
183206
*/
184207
const init = (left, right, node) => {
208+
// 세그먼트 트리의 리프 노드인 경우
185209
if (left === right) {
186210
tree[node] = arr[left];
187211
return;
@@ -197,11 +221,11 @@ const init = (left, right, node) => {
197221
* node가 표현하는 범위 arr[nodeLeft...nodeRight]가 주어질 때,
198222
* 이 범위와 arr[left...right]의 교집합의 구간 합을 구합니다.
199223
*
200-
* @param {number} left
201-
* @param {number} right
202-
* @param {number} node
203-
* @param {number} nodeLeft
204-
* @param {number} nodeRight
224+
* @param {number} left 구하고자 하는 구간 합의 시작 인덱스
225+
* @param {number} right 구하고자 하는 구간 합의 끝 인덱스
226+
* @param {number} node 현재 탐색 중인 세그먼트 트리의 노드 번호
227+
* @param {number} nodeLeft 현재 노드가 표현하는 구간의 시작 인덱스
228+
* @param {number} nodeRight 현재 노드가 표현하는 구간의 끝 인덱스
205229
* @returns 구간 합
206230
*/
207231
const query = (left, right, node, nodeLeft, nodeRight) => {
@@ -226,11 +250,11 @@ const query = (left, right, node, nodeLeft, nodeRight) => {
226250
* arr[index] = newValue로 바뀌었을 때
227251
* node를 루트로 하는 세그먼트 트리를 갱신합니다.
228252
*
229-
* @param {number} index
230-
* @param {number} newValue
231-
* @param {number} node
232-
* @param {number} nodeLeft
233-
* @param {number} nodeRight
253+
* @param {number} index 값이 바뀐 배열의 인덱스
254+
* @param {number} newValue 새로운 값
255+
* @param {number} node 현재 탐색 중인 세그먼트 트리의 노드 번호
256+
* @param {number} nodeLeft 현재 노드가 표현하는 구간의 시작 인덱스
257+
* @param {number} nodeRight 현재 노드가 표현하는 구간의 끝 인덱스
234258
*/
235259
const update = (index, newValue, node, nodeLeft, nodeRight) => {
236260
// index가 노드가 표현하는 구간과 상관없는 경우에는 무시합니다.
@@ -262,6 +286,8 @@ console.log(query(3, 5, 1, 0, arr.length - 1)); // 8 + 9 + 11 = 28
262286

263287
## Example
264288

289+
- <a href="" target="_blank">TODO</a>
290+
265291
## 참고 자료
266292

267293
- <a href="https://www.yes24.com/product/goods/8006522" target="_blank">알고리즘 문제 해결 전략 세트 | 구종만 | 인사이트(insight) - 예스24</a>

0 commit comments

Comments
 (0)