Segment Tree
in Algorithms
Segment Tree에 대해 설명하는 페이지입니다.
Environment
- OS: Windows 11
목차
Introduction
- Definition
- 구간 트리(Segment Tree)는 저장된 자료들을 적절히 전처리해 그들에 대한 질의들을 빠르게 대답할 수 있게 구현한 트리이다.
- 구간 트리는 흔히 일차원 배열의 특정 구간에 대한 질문을 빠르게 대답하는데 사용된다.
- 구간 트리의 기본적인 아이디어는 주어진 배열의 구간들을 표현하는 이진 트리를 만드는 것이다.
- 구간 최소 쿼리(Range Minimum Query, RMQ)
- 구간 최소 쿼리는 특정 구간의 최소치를 찾는 문제에서 주로 사용된다.
- 구간 최소 쿼리는 배열로 표현된다.
- Time Complexity
- 특정 구간에 대한 질의:
O(lgN)
- 초기화 과정의 시간 복잡도:
O(N)
- 구간 트리의 update:
O(lgN)
- 특정 구간에 대한 질의:
How to Use
- 배열의 구간 최소 쿼리를 해결하기 위한 구간 트리의 구현
// TypeScript class RMQ { private _size: number; // 배열의 길이 private _rangeMin: Array<number>; // 각 구간의 최소치 constructor(arr: Array<number>) { this._size = arr.length; this._rangeMin = Array<number>(this._size * 4); this.init(arr, 0, this._size - 1, 1); } /** * node 노드가 arr[left..right] 배열을 표현할 때 * node를 루트로 하는 서브트리를 초기화하고, 이 구간의 최소치를 반환한다. * @param arr 배열 * @param left 시작 인덱스 * @param right 끝 인덱스 * @param node 루트 */ private init = ( arr: Array<number>, left: number, right: number, node: number ): number => { if (left === right) { this._rangeMin[node] = arr[left]; return this._rangeMin[node]; } const mid = Math.floor((left + right) / 2); const leftMin = this.init(arr, left, mid, node * 2); const rightMin = this.init(arr, mid + 1, right, node * 2 + 1); this._rangeMin[node] = Math.min(leftMin, rightMin); return this._rangeMin[node]; }; /** * node가 표현하는 범위 arr[nodeLeft..nodeRight]가 주어질 때, * 이 범위와 arr[left..right]의 교집합의 최소치를 구한다. */ private _query = ( left: number, right: number, node: number, nodeLeft: number, nodeRight: number ): number => { // 두 구간이 겹치지 않으면 아주 큰 값을 반환한다: 무시됨 if (right < nodeLeft || nodeRight < left) { return Number.MAX_SAFE_INTEGER; } // node가 표현하는 범위가 arr[left..right]에 완전히 포함되는 경우 if (left <= nodeLeft && nodeRight <= right) { return this._rangeMin[node]; } // 양쪽 구간을 나눠서 푼 뒤 결과를 합친다. const mid = Math.floor((nodeLeft + nodeRight) / 2); return Math.min( this._query(left, right, node * 2, nodeLeft, mid), this._query(left, right, node * 2 + 1, mid + 1, nodeRight) ); }; /** * query()을 외부에서 호출하기 위한 인터페이스 */ public query = (left: number, right: number): number => { return this._query(left, right, 1, 0, this._size - 1); }; /** * arr[index] = newValue로 바뀌었을 때 node를 루트로 하는 * 구간 트리를 갱신하고 노드가 표현하는 구간의 최소치를 반환한다. * @param index * @param newValue * @param node * @param nodeLeft * @param nodeRight */ private _update = ( index: number, newValue: number, node: number, nodeLeft: number, nodeRight: number ): number => { // index가 노드가 표현하는 구간과 상관없는 경우엔 무시한다. if (index < nodeLeft || nodeRight < index) { return this._rangeMin[node]; } // 트리의 leaf까지 내려온 경우 if (nodeLeft === nodeRight) { this._rangeMin[node] = newValue; return newValue; } const mid = Math.floor((nodeLeft + nodeRight) / 2); return Math.min( this._update(index, newValue, node * 2, nodeLeft, mid), this._update(index, newValue, node * 2 + 1, mid + 1, nodeRight) ); }; /** * update()을 외부에서 호출하기 위한 인터페이스 */ public update = (index: number, newValue: number): number => { return this._update(index, newValue, 1, 0, this._size - 1); }; }
Examples