본문 바로가기

개발/알고리즘

펜윅 트리란?(Fenwick Tree, Binary Indexed Tree)

펜윅 트리란 효과적으로 요소를 업데이트하고 수 배열의 구간합을 계산할 수 있는 자료구조 입니다.

세그먼트 트리와 다른점은 오른쪽 자식이 필요없다는 점 입니다.

부모노드와 왼쪽 자식노드의 값을 알고있으면 자연스레 오른쪽 자식노드의 값도 알 수 있기 때문입니다.  

 

펜윅트리를 사용하기위해서 배열을 하나 사용하여 정의할 수 있습니다.

L(i) : i를 2진수로 나타냈을 때 가장 마지막 1이 나타내는 값을 담은 L(i) 를 이용합니다.

십진수를 이진수로 나타낸다음, 이진수로 나타낸 값에서 마지막 1이 나오는 자리 수 의 값이 L(i) 의 값을 의미합니다.

십진수 이진수 마지막 1이 나오는 자리 수 마지막 1이 나오는 자리 수의 이진수 값 L(i)
3 11(2) 2^0 1 L(3) = 1
5 101(2) 2^0 1 L(5) = 1
6 110(2) 2^1 2 L(6) = 2
8 1000(2) 2^3 8 L(8) = 8
9 1001(2) 2^0 1 L(9) = 1
10 1010(2) 2^1 2 L(10) = 2
11 1011(2) 2^0 1 L(11) = 1
12 1100(2) 2^2 4 L(12) = 4
16 10000(2) 2^4 16 L(16) = 16

L(i)를 계산하는 방법은 2가지가 있습니다.

첫번째 방법은 위의 방법을 그대로 사용하는 방법입니다. 시간복잡도는 O(logi) 입니다.

두번째 방법은 i & -i 비트연산을 해주는 방법입니다. 시간복잡도는 O(n) 입니다. 

-i = ~i + 1과 같기 때문에 i & -i 비트연산을 해주면 마지막 1이 나오는 자리 수를 뽑아올 수 있습니다.

 

i까지 L(i)개의 합을 Tree[i] 배열을 이용하여 나타낼 수 있습니다.

i 가 12일 경우 L(i) = 4 이고, Tree[12] = A[9] + A[10] + A[11] + A[12] 라고 할 수  있습니다. 

펜윅트리는 사실 구간합을 구할 수 없습니다. 첫번째수부터 i번째 수 까지의 합을 구하는 것만 할 수 있습니다.

A[1] 부터 A[13] 까지의 합은 tree[1101(2)] + tree[1100(2)] + tree[1000(2)] 와 같습니다.

그 이유는 다음과 같습니다.

13 = 1101(2) -> L(13) = 1 -> tree[13] = A[13]

12(13 - L(13)) = 1100(2) -> L(12) = 4 -> tree[12] = A[9] + ... + A[12]

8(12-L(12)) = 1000(2) -> L(8) = 8 -> tree[8] = A[1] + ... + A[8]

 

이를 이용하여 i ~ j 번째까지 구간합을 구할 수 있게 됩니다. 

A[1] ~ A[j] 까지 합을 구하고, A[1] ~ A[i-1] 까지의 합을 구하여서 이를 빼주면 됩니다.

(이러한 이유때문에 최소값을 구할 수 없게됩니다.)

 

이를 코드로 나타내면 아래와 같습니다.

int sum(int i)
{
	int answer = 0;
    while(i > 0)
    {
    	answer += tree[i];
        i -= (i & -i);
    }
    return answer;
}
int update(int i, int num)
{
	while(i <= n)
    {
    	tree[i] += num;
        i += (i & -i);
    }
}

 

*쿼리 i~j 까지 구할 때 시간, 공간복잡도

  선처리 공간복잡도 쿼리 변경 비고
그냥 수행할 때 x   O(N)    
Sqrt decomposition N sqrt(N) sqrt(N) sqrt(N)  
DP NlogN NlogN logN logN  
세그먼트 트리 NlogN NlogN logN logN  
누적합 N N 1 N 합에 대해서만 사용 가능
펜윅트리(BIT) NlogN N logN logN 합에 대해서만 사용 가능

'개발 > 알고리즘' 카테고리의 다른 글

백준 16118 달빛여우 C++  (0) 2021.05.31
백준 9370 미확인 도착지 C++  (0) 2021.05.29
누적합(Prefix Sum) 이란?  (0) 2021.05.27
백준 5719 거의최단경로 C++  (0) 2021.05.27
백준 17612 쇼핑몰 C++  (0) 2021.05.16