본문 바로가기
한국으로/알고리즘과 자료구조

세그먼트 트리 (Segment Tree)

by 영킴. 2018. 7. 3.

세그먼트 트리 (Segment Tree)

세그먼트 트리란?

구간에 대한 정보를 저장함.

특정 구간의 최적화된 답을 구하는 문제에 사용.
부분 구간의 최적 답들 중에서 최적 답을 구할 수 있음.

<시간 복잡도>
자료 저장: O(n log n)
특정 구간 최적해 검색: O(log n + k)

SUM TREE를 배열 기반으로 짜면, 노드마다 자식 노드의 TOTAL SUM 정보를 저장하게 됨.

구현

TREE 초기 자료 구성:
원본 데이터를 인자로 받아서 LEAF NODE를 찾아 하나씩 저장하는 프로세스.

배열의 크기 잡는 법:
예를 들어 데이터가 1000개라면, 1000 < 2^10 이므로, TREE LEVEL은 N = 11개가 만들어져야 함.

TREE[1<< N] 로 배열 크기를 잡아주자.
이렇게 넉넉하게 잡아주는 이유는, 배열에서 가장 작은 index의 부모 노드의 자식 노드가 생기면, TREE LEVEL이 한 개 더 늘어나기 때문임.
 
PARENT NODE = NODE
LEFT CHILD NODE = NODE * 2
RIGHT CHILD NODE = NODE * 2 + 1


Sample Code

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//SEGMENT TREE
void Update_New_Data(int node, int s, int e, int idx, int data)
{
    int m;
    
    if (s == e)
    {
        tree[node] = data;
        return;
    }
 
    m = (s + e) / 2;
    if (idx <= m) Update_New_Data(node * 2, s, m, idx, data);
    else Update_New_Data(node * 2 + 1, m + 1, e, idx, data);
 
    tree[node] = tree[node * 2* tree[node * 2 + 1];
}
 
int Query(int node, int s, int e, int qs, int qe)
{
    int m, left, right;
 
    if (qs <= s && e <= qe) return tree[node];
    else if (qs > e || s > qe)  return 0;
 
    m = (s + e);
    left = Query(node * 2, s, m, qs, qe);
    right = Query(node * 2 + 1, m + 1, e, qs, qe);
 
    return left * right;
}
 
void Update_Add(int node, int s, int e, int us, int ue, int add)
{
    int m;
 
    if (s == e)
    {
        if (us <= s && e <= ue) tree[node] += add;
        return;
    }
    else if (us > e || s > ue) return;
 
    m = (s + e) / 2;
    Update_Add(node * 2, s, m, us, ue, add);
    Update_Add(node * +1, m + 1, e, us, ue, add);
 
    tree[node] = tree[node * 2* tree[node * 2 + 1];
 
}
 
cs


Update_New_Data 함수에서 ( s == e ) 일 경우 데이터를 저장 하는 이유:

배열을 m으로 반으로 쪼개면서 검색하다가 마지막 LEAF NODE를 발견했을 때 데이터를 삽입한다는 것.

부모 노드의 정보를 업데이트 함수 마지막에 갱신해주는 과정 필수.


Query 함수로 부분( qs ~ qe )의 정보를 구할 수 있다. 

위 샘플 코드의 트리는 MULTIPLE TREE 라서 노드의 곱을 구하고 있다.


Update_Add 함수는 노드의 정보를 새로운 값으로 업데이트하는 함수.

LEAF NODE에 도달했을 때, 정보를 바꾸기 원하는 구간( us ~ ue )인지도 확인한 뒤에 업데이트해줘야 한다.

그리고 당연히 부모 노드도 새로운 정보를 바탕으로 업데이트해야 함.




Sample Code 2 (Lazy Propagation 추가)

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
32
33
34
35
36
37
38
39
void Propagate(int node, int s, int e)
{
    if (lazy[node] == 0return;
 
    //자식 노드에 전달
    if (s != e)
    {
        lazy[node * 2+= lazy[node];
        if (node * 2 + 1 <= LASTNODE) lazy[node * 2 + 1+= lazy[node];
    }
 
 
    //자기 노드 업데이트
    tree[node] += (e - s + 1)*lazy[node];
    lazy[node] = 0;
}
 
 
void Update_Add_Lazy(int node, int s, int e, int us, int ue, int add)
{
    int m;
 
    Propagate(node, s, e);
 
    if (us <= s && ue >= e)
    {
        lazy[node] += add;
        Propagate(node, s, e);
        return;
    }
    else if (us > e || s > ue) return;
 
    m = (s + e) / 2;
    Update_Add_Lazy(node * 2, s, m, us, ue, add);
    Update_Add_Lazy(node * 2 + 1, m + 1, e, us, ue, add);
 
    tree[node] = tree[node * 2+ tree[node * 2 + 1];
}
 
cs

LAZY PROPAGATION

  • 이건 자료를 업데이트할 최악의 경우 O(N log N) 복잡도가 있는데, 이를 방지하고 시간 복잡도를 O(log N) 으로 고정시키기 위한 알고리즘이다.

  • UPDATE , 당장 필요한 변경만 진행하고 자식 노드에 대해서는 다음에 해당 노드를 재탐색시 변경하는 전략임.




'한국으로 > 알고리즘과 자료구조' 카테고리의 다른 글

병합 정렬 (Merge Sort)  (0) 2018.07.04
힙 정렬 (Heap Tree Sort)  (0) 2018.07.03