본문 바로가기

공부합시다

세그먼트 트리

마주하고 말았다. 세그먼트 트리

 

세그먼트 트리

구간을 반복해서 2등분 한 뒤 각각의 잘린 구간이 자신의 구간인 대푯값을 저장하고 관리하게 만드는 자료구조

 

query: O(logN)

update: O(logN)

 

query는 정확히는 O(2logN)

레벨당 2개의 노드만 건드리게 된다.

 

구현

full binary tree 형태를 가지기 때문에 힙처럼 구간의 모든 값을 배열에 저장할 수 있다.

어떤 노드의 번호가 idx면 왼쪽 자식은 2 * idx, 오른쪽 자식은 2 * idx + 1

배열의 크기는 4 * n 이상으로 설정해야 한다

 

int query(int l, int r, int node, int nodeL, int nodeR) {
    if (l <= nodeL && nodeR <= r)
        return data[node];
    
    int mid = (nodeL + nodeR) / 2;
    
    if (mid < l)
        return query(l, r, node * 2 + 1, mid + 1, nodeR);
    if (mid + 1 > r)
        return query(l, r, node * 2, nodeL, mid);
    return query(l, r, node * 2, nodeL, mid) + query(l, r, node * 2 + 1, mid + 1, nodeR);
}

쿼리 범위: l, r

노드가 나타내는 범위: nodeL, nodeR

 

노드가 나타내는 범위가 쿼리 범위 l ~r에 포함된다면 해당 노드의 값을 그대로 리턴해준다

쿼리할 구간이 좌측 절반, 또는 우측 절반만 겹칠 경우 해당 절반만 쿼리해준다

나머지 경우는 왼쪽 오른쪽 자신 전부를 다 봐야 하는 경우라 양쪽 쿼리한 결과를 더해준다

 

int update(int idx, int val, int node, int nodeL, int nodeR) {
    if (idx < nodeL || idx > nodeR)
        return v[node];
    
    if (nodeL == nodeR)
        return v[node] = val;
    
    int mid = (nodeL + nodeR) / 2;
    return v[node] = update(idx, val, node * 2, nodeL, mid) +
        update(idx, val, node * 2 + 1, mid + 1, nodeR);
}

- 업데이트 할 인덱스와 상관 없는 경우 갱신할게 없으므로 원래 값 리턴후 종료

- 리프노드에 도달했을 경우 해당 리프노드의 값을 새 값으로 갱신

- 그게 아니면 양쪽 자식 똑같이 갱신해주고 바뀐 값으로 내 노드도 갱신

 

int init(vector<int> &arr, int node, int start, int end) {
    int mid = (start + end) / 2;
    
    if (start == end)
        return v[node] = arr[start];
    return v[node] = init(arr, node * 2, start, mid) + init(arr, node * 2 + 1, mid + 1, end);
}

특정한 초기값으로 설정해줄 때 쓸 수 있다.

 

 

'공부합시다' 카테고리의 다른 글

유클리드 호제법  (0) 2024.11.16
외판원순회  (0) 2023.07.22
플로이드 와샬 알고리즘  (0) 2022.12.17
순열  (1) 2022.10.01
offset 대신 포인터로 사용하기  (0) 2022.08.31