|
| 1 | +--- |
| 2 | +title: Fenwick Tree |
| 3 | +tags: |
| 4 | + - Data Structures |
| 5 | + - Fenwick Tree |
| 6 | + - Binary Indexed Tree |
| 7 | + - BIT |
| 8 | +--- |
| 9 | + |
| 10 | +Binary Indexed Tree olarak da bilinen Fenwick Tree, [Prefix Sum](prefix-sum.md) ve [Sparse Table](sparse-table.md) yapılarına benzer bir yapıda olup dizi üzerinde değişiklik yapabilmemize olanak sağlayan bir veri yapısıdır. Fenwick Tree'nin diğer veri yapılarına göre en büyük avantajı pratikte daha hızlı olması ve hafıza karmaşıklığının $\mathcal{O}(N)$ olmasıdır. Ancak Fenwick Tree'de sadece prefix cevapları (veya suffix cevapları) saklayabildiğimizden aralıklarda minimum, maksimum ve EBOB gibi bazı sorguların cevaplarını elde edemeyiz. |
| 11 | + |
| 12 | +## Yapısı ve Kuruluşu |
| 13 | + |
| 14 | +$g(x)$, $x$ sayısının bit gösteriminde yalnızca en sağdaki bitin 1 olduğu tam sayı olsun. Örneğin $20$'nin bit gösterimi $(10100)_2$ olduğundan $g(20)=4$'tür. Çünkü ilk kez sağdan $3.$ bit $1$'dir ve $(00100)_2=4$'tür. Fenwick Tree'nin $x$ indeksli düğümünde, <span style="white-space: nowrap">$x - g(x) + 1$</span> indeksli elemandan $x$ indeksli elemana kadar olan aralığın cevabını saklayacak şekilde kurulur. |
| 15 | + |
| 16 | +<figure markdown="span"> |
| 17 | +{ width="80%" } |
| 18 | +<figcaption>$8$ uzunluğundaki bir dizi için kurulmuş Fenwick Tree yapısı</figcaption> |
| 19 | +</figure> |
| 20 | + |
| 21 | + |
| 22 | +## Sorgu Algoritması |
| 23 | + |
| 24 | +Herhangi bir $[1,x]$ aralığı için sorgu algoritması sırası ile şu şeklide çalışır: |
| 25 | + |
| 26 | +1. Aradığımız cevaba $[x - g(x) + 1,x]$ aralığının cevabını ekle. |
| 27 | +2. $x$'in değerini $x - g(x)$ yap. Eğer $x$'in yeni değeri $0$'dan büyük ise $1.$ işlemden hesaplamaya devam et. |
| 28 | + |
| 29 | +$[1,x]$ aralığının cevabını hesaplamak için yapılan işlem sayısı $x$ sayısının $2$'lik tabandaki yazılışındaki $1$ sayısına eşittir. Çünkü her döngüde $x$'ten $2$'lik tabandaki yazılışındaki en sağdaki $1$ bitini çıkartıyoruz. Dolayısıyla sorgu işlemimiz $\mathcal{O}(\log N)$ zaman karmaşıklığında çalışır. $[l,r]$ aralığının cevabını da $[1,r]$ aralığının cevabından $[1,l - 1]$ aralığının cevabını çıkararak kolay bir şekilde elde edebiliriz. |
| 30 | + |
| 31 | +> NOT: $g(x)$ değerini bitwise operatörlerini kullanarak aşağıdaki eşitlikle kolay bir şekilde hesaplayabiliriz: |
| 32 | +> \\[g(x) = x \ \& \ (-x)\\] |
| 33 | +
|
| 34 | +## Eleman Güncelleme Algoritması |
| 35 | + |
| 36 | +Dizideki $x$ indeksli elemanının değerini güncellemek için kullanılan algoritma şu şeklide çalışır: |
| 37 | + |
| 38 | +- Ağaçta $x$ indeksli elemanı içeren tüm düğümlerin değerlerini güncelle. |
| 39 | + |
| 40 | +Fenwick Tree'de $x$ indeksli elemanı içeren maksimum $\log(N)$ tane aralık olduğundan güncelleme algoritması $\mathcal{O}(\log N)$ zaman karmaşıklığında çalışır. |
| 41 | + |
| 42 | +## Örnek Kod Parçaları |
| 43 | + |
| 44 | +```c++ |
| 45 | +const int n; |
| 46 | +int tree[n + 1], a[n + 1]; |
| 47 | + |
| 48 | +void add(int val, int x) { // x indeksli elemanin degerini val degeri kadar artirir. |
| 49 | + // x indeksinin etkiledigi butun dugumleri val degeri kadar artirir. |
| 50 | + while (x <= n) { |
| 51 | + tree[x] += val; |
| 52 | + x += x & (-x); |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +int sum(int x) { // 1 indeksli elemandan x indeksli elemana |
| 57 | + int res = 0; // kadar olan sayilarin toplamini verir. |
| 58 | + while (x >= 1) { |
| 59 | + res += tree[x]; |
| 60 | + x -= x & (-x); |
| 61 | + } |
| 62 | + return res; |
| 63 | +} |
| 64 | + |
| 65 | +int query(int l, int r) { // [l,r] araligindaki elemanlarin toplamini verir. |
| 66 | + return sum(r) - sum(l - 1); |
| 67 | +} |
| 68 | + |
| 69 | +void build() { // a dizisi uzerine fenwick tree yapisini kuruyoruz. |
| 70 | + for (int i = 1; i <= n; i++) |
| 71 | + add(a[i], i); |
| 72 | +} |
| 73 | +``` |
| 74 | +
|
| 75 | +Fenwick Tree veri yapısı ile ilgili örnek bir probleme [buradan](https://www.spoj.com/problems/CSUMQ) ulaşabilirsiniz. |
| 76 | +
|
| 77 | +## Aralık Güncelleme ve Eleman Sorgu |
| 78 | +
|
| 79 | +Bir $a$ dizisi üzerinde işlemler yapacağımızı varsayalım daha sonra $a$ dizisi $b$ dizisinin prefix sum dizisi olacak şekilde bir $b$ dizisi tanımlayalım. Başka bir deyişle $a_i = \displaystyle\sum_{j=1}^{i} {b_j} $ olmalıdır. Sonradan oluşturduğumuz $b$ dizisi üzerine Fenwick Tree yapısını kuralım. $[l,r]$ aralığındaki her elemana |
| 80 | +$x$ değerini eklememiz için uygulamamız gereken işlemler: |
| 81 | +
|
| 82 | +- $b_l$ değerini $x$ kadar artır. Böylelikle $l$ indeksli elemandan dizinin sonuna kadar tüm elemanların değeri $x$ kadar artmış olur. |
| 83 | +- $b_{r + 1}$ değerini $x$ kadar azalt. Böylelikle $r + 1$ indeksli elemandan dizinin sonuna kadar tüm elemanların değeri $x$ kadar azalmış olur. Bu işlemelerin sonucunda sadece $[l,r]$ aralığındaki elemanların değeri $x$ kadar artmış olur. |
| 84 | +
|
| 85 | +### Örnek Kod Parçaları |
| 86 | +
|
| 87 | +```c++ |
| 88 | +const int n; |
| 89 | +int a[n + 1], b[n + 1]; |
| 90 | +
|
| 91 | +void add(int val, int x) { // x indeksli elemanin degerini val degeri kadar artirir. |
| 92 | + while (x <= n) { |
| 93 | + tree[x] += val; |
| 94 | + x += x & (-x); |
| 95 | + } |
| 96 | +} |
| 97 | +
|
| 98 | +int sum(int x) { // 1 indeksli elemandan x indeksli elemana |
| 99 | + int res = 0; // kadar olan sayilarin toplamini verir. |
| 100 | + while (x >= 1) { |
| 101 | + res += tree[x]; |
| 102 | + x -= x & (-x); |
| 103 | + } |
| 104 | + return res; |
| 105 | +} |
| 106 | +void build() { |
| 107 | + for (int i = 1; i <= n; i++) |
| 108 | + b[i] = a[i] - a[i - 1]; // b dizisini olusturuyoruz. |
| 109 | +
|
| 110 | + for (int i = 1; i <= n; i++) |
| 111 | + add(b[i], i); // b dizisi uzerine fenwick tree kuruyoruz. |
| 112 | +} |
| 113 | +
|
| 114 | +void update(int l, int r, int x) { |
| 115 | + add(x, l); |
| 116 | + add(-x, r + 1); |
| 117 | +} |
| 118 | +
|
| 119 | +void query(int x) { return sum(x); } |
| 120 | +``` |
0 commit comments