Skip to content

Commit b72140a

Browse files
authored
Implement Fenwick Tree with basic operations
This implementation supports efficient prefix sum queries and point updates. It includes methods for updating values, calculating prefix sums, range sums, and setting specific values in the Fenwick Tree.
1 parent d071d83 commit b72140a

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
"""Fenwick Tree (Binary Indexed Tree) Data Structure.
2+
3+
A Fenwick Tree is a data structure that can efficiently update elements
4+
and calculate prefix sums in a table of numbers. It is also known as a
5+
Binary Indexed Tree (BIT).
6+
7+
Time Complexity:
8+
- Update: O(log n)
9+
- Query (prefix sum): O(log n)
10+
- Construction: O(n log n)
11+
12+
Space Complexity: O(n)
13+
"""
14+
15+
16+
class FenwickTree:
17+
"""Fenwick Tree implementation for range sum queries.
18+
19+
This implementation supports efficient prefix sum queries and point updates.
20+
The tree uses 1-based indexing internally for simpler implementation.
21+
22+
Attributes:
23+
size: Size of the input array
24+
tree: List storing the Fenwick tree values
25+
26+
>>> ft = FenwickTree(5)
27+
>>> ft.update(0, 3)
28+
>>> ft.update(1, 2)
29+
>>> ft.update(2, 5)
30+
>>> ft.update(3, 1)
31+
>>> ft.update(4, 4)
32+
>>> ft.prefix_sum(2)
33+
10
34+
>>> ft.range_sum(1, 3)
35+
8
36+
>>> ft2 = FenwickTree([1, 3, 5, 7, 9])
37+
>>> ft2.prefix_sum(2)
38+
9
39+
>>> ft2.range_sum(1, 3)
40+
15
41+
"""
42+
43+
def __init__(self, size_or_array: int | list[int]) -> None:
44+
"""Initialize Fenwick Tree.
45+
46+
Args:
47+
size_or_array: Either size of array (int) or initial array values (list)
48+
49+
>>> ft = FenwickTree(5)
50+
>>> len(ft.tree)
51+
6
52+
>>> ft2 = FenwickTree([1, 2, 3])
53+
>>> ft2.prefix_sum(2)
54+
6
55+
"""
56+
if isinstance(size_or_array, int):
57+
self.size = size_or_array
58+
self.tree = [0] * (self.size + 1) # 1-indexed
59+
else:
60+
self.size = len(size_or_array)
61+
self.tree = [0] * (self.size + 1)
62+
for i, val in enumerate(size_or_array):
63+
self.update(i, val)
64+
65+
def update(self, index: int, delta: int) -> None:
66+
"""Add delta to element at given index.
67+
68+
Args:
69+
index: Index to update (0-based)
70+
delta: Value to add to the element
71+
72+
>>> ft = FenwickTree(5)
73+
>>> ft.update(0, 5)
74+
>>> ft.prefix_sum(0)
75+
5
76+
>>> ft.update(0, 3)
77+
>>> ft.prefix_sum(0)
78+
8
79+
"""
80+
index += 1 # Convert to 1-based indexing
81+
while index <= self.size:
82+
self.tree[index] += delta
83+
index += index & (-index) # Add last set bit
84+
85+
def prefix_sum(self, index: int) -> int:
86+
"""Calculate sum of elements from 0 to index (inclusive).
87+
88+
Args:
89+
index: End index for prefix sum (0-based, inclusive)
90+
91+
Returns:
92+
Sum of elements from index 0 to given index
93+
94+
>>> ft = FenwickTree([1, 2, 3, 4, 5])
95+
>>> ft.prefix_sum(0)
96+
1
97+
>>> ft.prefix_sum(2)
98+
6
99+
>>> ft.prefix_sum(4)
100+
15
101+
"""
102+
index += 1 # Convert to 1-based indexing
103+
result = 0
104+
while index > 0:
105+
result += self.tree[index]
106+
index -= index & (-index) # Remove last set bit
107+
return result
108+
109+
def range_sum(self, left: int, right: int) -> int:
110+
"""Calculate sum of elements in range [left, right].
111+
112+
Args:
113+
left: Left boundary of range (0-based, inclusive)
114+
right: Right boundary of range (0-based, inclusive)
115+
116+
Returns:
117+
Sum of elements in the given range
118+
119+
>>> ft = FenwickTree([1, 2, 3, 4, 5])
120+
>>> ft.range_sum(0, 2)
121+
6
122+
>>> ft.range_sum(2, 4)
123+
12
124+
>>> ft.range_sum(1, 1)
125+
2
126+
"""
127+
if left == 0:
128+
return self.prefix_sum(right)
129+
return self.prefix_sum(right) - self.prefix_sum(left - 1)
130+
131+
def set_value(self, index: int, value: int) -> None:
132+
"""Set element at index to given value.
133+
134+
Args:
135+
index: Index to set (0-based)
136+
value: New value to set
137+
138+
>>> ft = FenwickTree([1, 2, 3, 4, 5])
139+
>>> ft.set_value(2, 10)
140+
>>> ft.prefix_sum(2)
141+
13
142+
>>> ft.range_sum(2, 2)
143+
10
144+
"""
145+
current_value = self.range_sum(index, index)
146+
delta = value - current_value
147+
self.update(index, delta)
148+
149+
150+
if __name__ == "__main__":
151+
import doctest
152+
153+
doctest.testmod()

0 commit comments

Comments
 (0)