[알고리즘 특강] 9. 세그먼트 트리(Segment Tree)
1. 세그먼트 트리의 정의와 필요성
1.1 세그먼트 트리란?
- 정의: 구간 쿼리를 효율적으로 처리하기 위한 트리 자료구조
- 특징:
- 구간 합, 최대값, 최소값 등의 연산을 O(logN)에 처리
- 데이터 업데이트도 O(logN)에 가능
- 메모리는 원본 배열의 4배 정도 필요
1.2 활용 사례
- 구간 합 계산
- 구간 최대/최소값 찾기
- 구간 곱 계산
- 히스토그램에서 가장 큰 직사각형 찾기
2. 세그먼트 트리 구조
2.1 기본 구조
세그먼트 트리의 기본 구조
2.2 구조적 특징
1. Complete Binary Tree 형태
2. 리프 노드를 제외한 모든 노드는 두 자식의 값을 합친 결과
3. 높이: O(logN)
4. 필요한 노드 수: 4N
3. 세그먼트 트리 구현
3.1 구간 합 예제
원본 배열: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
3.1.1 단순 계산 방식 (O(N))
// 구간 [6,9]의 합
int sum = 0;
for(int i = 6; i <= 9; i++) sum += arr[i]; // 7+8+9+10 = 34
3.1.2 누적 합 방식 (O(1), 갱신 O(N))
// 누적합 배열
int cumsum[10] = {1, 3, 6, 10, 15, 21, 28, 36, 45, 55};
// 구간 [6,9]의 합
int sum = cumsum[9] - cumsum[5]; // 55 - 21 = 34
3.1.3 세그먼트 트리 방식 (O(logN))
[1-10]:55 / \ [1-5]:15 [6-10]:40 / \ / \ [1-3]:6 [4-5]:9 [6-8]:24 [9-10]:19
3.2 기본 연산 구현
3.2.1 트리 초기화
int init(int* tree, int node, int s, int e) {
if(s == e)
return tree[node] = arr[s];
int m = (s + e) / 2;
int a = init(tree, node*2, s, m);
int b = init(tree, node*2 + 1, m+1, e);
return tree[node] = a + b;
}
3.2.2 구간 합 쿼리
int query(int* tree, int node, int ts, int te, int qs, int qe) {
// 완전히 포함되는 경우
if (ts >= qs && qe >= te) return tree[node];
// 겹치지 않는 경우
if (te < qs || qe < ts) return 0;
// 부분적으로 겹치는 경우
int m = (ts + te) / 2;
return query(tree, node*2, ts, m, qs, qe) +
query(tree, node*2+1, m+1, te, qs, qe);
}
3.2.3 값 업데이트
int update(int* tree, int node, int s, int e, int idx, int diff) {
if (idx < s || e < idx) return tree[node];
tree[node] += diff;
if (s == e) return tree[node];
int m = (s + e) / 2;
int a = update(tree, node*2, s, m, idx, diff);
int b = update(tree, node*2+1, m+1, e, idx, diff);
return tree[node] = a + b;
}
4. 세그먼트 트리 최적화
4.1 시간 복잡도 비교
| 연산 | 일반 배열 | 누적 합 | 세그먼트 트리 | |——|———–|———|—————| | 구간 쿼리 | O(N) | O(1) | O(logN) | | 값 갱신 | O(1) | O(N) | O(logN) | | 공간 복잡도 | O(N) | O(N) | O(N) |
4.2 최적화 팁
- 메모리 최적화
- 완전 이진 트리 특성을 이용해 배열 크기 최적화
- 비트 연산으로 노드 인덱스 계산 최적화
- 재귀 제거
- 반복문으로 구현하여 함수 호출 오버헤드 제거
- 특히 업데이트 연산에서 효과적
💡 참고: 세그먼트 트리는 Lazy Propagation을 통해 구간 업데이트도 O(logN)에 수행할 수 있습니다.
댓글남기기