Segment Tree


세그먼트 트리란?

  • 주어진 데이터의 구간합과 데이터 업데이트를 빠르게 수행하기 위해 고안해낸 자료구조
    • 기존 구간합은 데이터가 자주 업데이트 되는 경우 성능이 좋지 않음
  • 인덱스 트리의 하나


언제 쓸까?

  • 구간합 구하는데, 업데이트가 발생할 때
  • 업데이트 발생하지 않으면 굳이 세그먼트 트리로 풀 필요가 없음



구현

  • 이진트리로 만듦
    • 이진트리 : 자식이 두 개
  • 리프노드만 원본 데이터가 들어갈 것



1. 트리 초기화


  • 트리의 크기 구하기
    • 인덱스 트리를 1차원 배열로 만들고 끝에서부터 채워넣기 위해
    • ⭐️ 트리의 크기 : 2^k * 2
      • 데이터의 개수가 N일 때, k는 2^k >= N을 만족하는 k의 최솟값
      • 0번째 인덱스는 사용하지 않음 (0번째를 포함한 크기를 구한 것)
  • 리프 노드에 값들을 채워줌
    • 시작 노드 : 2^k
  • 리프 노드 외의 값 채워주기
    • 채워야 하는 인덱스 N일 때, 자식 노드는 2N, 2N + 1 임을 이용

    ex. 구간합을 구할 때 tree[N] = tree[2N] + tree[2N + 1]


예제

  • 샘플 : {5, 8, 4, 3, 7, 2, 1, 6}
  • 트리의 크기 : k = 3 이므로 2^3 * 2 = 16
  • 리프 노드에 값 채우기
    • 시작 인덱스 : 8 (= 2^3)
    idx 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
                    5 8 4 3 7 2 1 6
  • 리프 노드 외의 노드 값 채우기

    ex. 구간합

    idx 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    0 36 20 16 13 7 9 7 5 8 4 3 7 2 1 6



2. 질의값 구하기


1) 질의 인덱스를 세그먼트 트리 인덱스로 변경

  • 세그먼트 트리 index = 질의 index + 2^k - 1


2) 질의값 구하기

(1) mod 연산으로 노드 선택 여부 결정

  • start_index % 2 == 1 일 때 해당 노드를 선택
  • end_index % 2 == 0 일 때 해당 노드를 선택

(2) start_index, end_index가 부모를 타고 올라가며 depth 변경

  • start_index depth 변경 : start_index = (start_index + 1) / 2
  • end_index depth 변경 : end_index = (end_index - 1) / 2

(3) end_index < start_index 일 때까지 (1) ~ (2)를 반복


💡 start_index, end_index depth 이동

  • start_index % 2 == 1 : start_index가 부모 노드의 오른쪽 노드

    -> 이 부모노드는 사용하면 안됨! (왼쪽 노드까지 포함되어 있으므로)

    start_index 노드는 따로 저장했다가 사용

  • start_index 노드를 따로 저장한 경우 start_index의 부모 노드의 다음 노드로 이동하기 위해 (start_index + 1) / 2 번째로 이동
  • end_index도 마찬가지



예제

  • 2 ~ 6번째의 구간합 (1번째가 시작)
idx 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 36 20 16 13 7 9 7 5 8 4 3 7 2 1 6


1) 세그먼트 트리의 인덱스로 변경

  • start_index = 2 + 7 = 9
  • end_index = 6 + 7 = 13


2) 부모 노드로 이동

  • 1회 이동

      start_index % 2 = 9 % 2 = 1 // 노드 선택
      end_index % 2 = 13 % 2 = 1 // 노드 미선택
      start_index = (start_index + 1) / 2 = 10 / 2 = 5
      end_index = (end_index - 1) / 2 = 12 / 2 = 6
    
  • 2회 이동

      start_index % 2 = 5 % 2 = 1 // 노드 선택
      end_index % 2 = 6 % 2 = 0 // 노드 선택
      start_index = (start_index + 1) / 2 = 6 / 2 = 3
      end_index = (end_index - 1) / 2 = 5 / 2 = 2
      // end_index < start_index 이므로 종료
    
  • 선택된 노드의 합을 구함

      sum = tree[9] + tree[5] + tree[6]
    



3. 데이터 업데이트


  • 부모로 올라가면서 값을 업데이트



관련 문제



참고 자료




Leave a comment