티스토리 뷰

Computer Science/Algorithm

Segment Tree

yeongminb 2024. 8. 17. 14:58

  Segment Tree는 어떤 배열에서 구간에 관한 정보를 저장해 놓는 자료구조이다. 누적합 배열을 사용할 때에는 $O(N)$으로 비교적 빠르긴 하지만 배열의 원소를 교체할 때에도 $O(N)$이므로 원소를 교체하는 쿼리가 많이 주어지는 경우에는 시간초과가 나기 쉽다.

 

 반면에 Segment Tree는 이진트리의 형태의 자료구조이므로, 원소 교체나 구간합을 구할 때의 시간복잡도가 $O(logN)$으로 매우 빠르다.

 

1. Segment Tree의 구조

 

 위 그림은 {1,9,3,8,4,5,5,9,10,3,4,5}라는 배열에 대한 구간합을 저장한 'Segment Tree'를 나타낸 것이다. 이진트리 형식으로 구성되어있으며 어떤 노드에 적혀 있는 숫자는 자식 노드들의 적혀 있는 숫자들의 합이라는 것을 알 수 있다. 또한 리프노드들에 적힌 숫자는 원래 배열의 숫자이다.

 

왼쪽에 있는 큰 서브트리

 

 자세히 보면 트리에서 왼쪽에 있는 큰 서브트리는 배열의 0~5까지의 인덱스에 대한 Segment Tree임을 알 수 있다. 이런식으로 Segment Tree는 재귀적으로 정의되어있다.

 

 

2. Segment Tree의 구현

(1) Init

  Segment Tree를 초기화해주는 함수이다. 구현은 재귀적으로 하며, Segment Tree에서 노드들의 값들은 Tree라는 배열에 저장해준다.

int arr[NMAX],tree[4*NMAX];

int init(int s, int e, int node){
    if(s==e) return tree[node] = arr[s];
    int mid = (s+e)/2;
    return tree[node] = init(s,mid,node*2) + init(mid + 1,e,node*2 + 1);
}

 

 $arr[]$는 원래 배열을 의미하고 $tree[]$는 Segment Tree를 의미한다. $s$와 $e$는 범위에 관한 정보를 담는 인덱스들이다. $s$는 시작하는 인덱스, $e$는 끝나는 인덱스이다. $s==e$인 경우는 범위가 1, 즉 리프노드를 의미하므로 해당 노드 번호에 $arr$의 값을 저장해준다. 참고로 $tree$의 메모리가 $arr$의 메모리의 4배인 이유는 $N=2^{k}$일 때 필요한 $tree$의 노드수가 $2^{k+1}-1$로 거의 2배이고, 2의 거듭제곱이 아닐 때에는 2배보다 크며 4배보다는 작기 때문에 충분하게 4배로 한 것이다.

 

 

(2) Sum

 Segment Tree를 이용해서 구간합을 구해주는 함수이다. 이 또한 재귀적으로 구현해준다.

int sum(int s, int e, int node, int l, int r){
    if(l>e || r<s) return 0; //범위 밖인 경우
    if(l<=s&&e<=r) return tree[node]; //범위와 완전히 일치하는 경우
    int mid = (s+e)/2;
    return sum(s,mid,node*2,l,r) + sum(mid+1,e,node*2+1,l,r);
}

 여기서 $l$과 $r$은 구간합을 구하고자하는 구간의 시작 인덱스와 끝 인덱스를 의마한다. 

 

 

(3) Update

 원소를 교체할 때 Segment Tree를 업데이트 해주는 함수이다. 리프노드를 교체한 뒤, 그 리프노드에 영향을 받는 모든 노드들에 값을 더해주거나 빼서 업데이트 해주는 방식이다.

arr[idx] = arr[idx] + val;

void update(int s,int e,int node,int idx,int val){
    if(idx<s||idx>e) return;
    tree[node] += val;
    if(s==e) return;
    int mid = (s+e)/2;
    update(s,mid,node*2,idx,val);
    update(mid+1,e,node*2+1,idx,val);
}

 여기서 $idx$는 교체하고자 하는 인덱스를 의미하고, $val$은 원래 값과 교체하려는 값의 차이이다. 주의해야할 점은 $arr[idx]$의 값도 같이 업데이트를 해주어야한다는 점이다.

'Computer Science > Algorithm' 카테고리의 다른 글

BitMasking DP를 이용한 TSP  (0) 2024.12.11
Strongly Connected Component  (3) 2024.08.31
Topology Sort  (0) 2024.07.15
Floyd-Warshall Algorithm  (0) 2024.07.15
Dijkstra Algorithm  (0) 2024.07.15
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함