개념
길이 N의 배열이 하나 있다고 가정하자.
1 |
|
이 길이 N 배열의 임의의 한 구간에 대한 합을 묻는 질문이 M회 들어온다면??
1 |
|
매번 배열을 참조해서 답변을 하기에는 무언가 비효율적이라는 것이 직관적으로 느껴질 것이다. 한 번의 질문마다 최대 N회 참조를 해야 한다.
-> 시간복잡도 O(NM)
prefix-sum array
여러 가지 개선방안이 있겠지만, 이런 상황에서 상당히 유용한 기법 중 하나는 배열의 누적 합을 미리 계산해두는 것이다
1 |
|
1 |
|
이렇게 n 번째 원소까지의 합이 저장된 배열이 있다면, 구간 A~B의 합을 구하고 싶을 때 ‘B까지의 합’ - ‘A-1까지의 합’을 구함으로써 O(1) 시간복잡도에 구할 수 있을 것이다.
1 |
|
그럼 질문의 형태가 다음과 같이 바뀐다면??
1 |
|
prefix-sum array 를 이용하면 Q 에 대한 대답은 O(1)만에 가능하겠지만, U 에 대한 배열 업데이트는 O(N)이 되므로, 결국 최종적인 시간복잡도는 O(NM)이 될 것이다
segment tree
prefix-sum array 가 1~N까지의 원소의 합을 저장했다면, segment array는 구간을 두 갈래로 나눠가면서 해당 구간의 합을 기록하는 식이다.
1 |
|
위의 배열을 예시로 들면, 만들어지는 segment tree는 다음과 같게 된다.
1 |
|
위 트리의 값을 유지시켜줌으로써 구간 A~B의 합을 구하는 데에 O(log(N)) 시간복잡도가 필요하게 된다.
트리의 값을 유지시켜주기 위해서는 U 를 처리해야 하는데, 이 역시 O(log(N)) 시간복잡도에 처리가 가능하다.
구현
1 |
|
변형 및 활용
기본 트리 [구간 합 구하기]
key가 튜플로 저장되는 트리 [최솟값과 최댓값]
두 개의 값을 저장하는 트리 [구간 합 구하기]