Skip to content

Commit f31fa4e

Browse files
itsamirhncclauss
andauthored
Fenwick Tree (TheAlgorithms#6319)
* Enhance fenwick_tree.py * Change update to add in fenwick_tree.py * Some changes * Fix bug * Add O(N) initializer to FenwickTree * Add get method to Fenwick Tree * Change tree in Fenwick Tree * Add rank query to FenwickTree * Add get_array method to FenwickTree * Add some tests * Update data_structures/binary_tree/fenwick_tree.py Co-authored-by: Christian Clauss <[email protected]> * Update data_structures/binary_tree/fenwick_tree.py Co-authored-by: Christian Clauss <[email protected]> * Update data_structures/binary_tree/fenwick_tree.py Co-authored-by: Christian Clauss <[email protected]> * change `List` to `list` Co-authored-by: Christian Clauss <[email protected]>
1 parent f46ce47 commit f31fa4e

File tree

1 file changed

+241
-22
lines changed

1 file changed

+241
-22
lines changed
+241-22
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,247 @@
1+
from copy import deepcopy
2+
3+
14
class FenwickTree:
2-
def __init__(self, SIZE): # create fenwick tree with size SIZE
3-
self.Size = SIZE
4-
self.ft = [0 for i in range(0, SIZE)]
5+
"""
6+
Fenwick Tree
7+
8+
More info: https://en.wikipedia.org/wiki/Fenwick_tree
9+
"""
10+
11+
def __init__(self, arr: list[int] = None, size: int = None) -> None:
12+
"""
13+
Constructor for the Fenwick tree
14+
15+
Parameters:
16+
arr (list): list of elements to initialize the tree with (optional)
17+
size (int): size of the Fenwick tree (if arr is None)
18+
"""
19+
20+
if arr is None and size is not None:
21+
self.size = size
22+
self.tree = [0] * size
23+
elif arr is not None:
24+
self.init(arr)
25+
else:
26+
raise ValueError("Either arr or size must be specified")
27+
28+
def init(self, arr: list[int]) -> None:
29+
"""
30+
Initialize the Fenwick tree with arr in O(N)
31+
32+
Parameters:
33+
arr (list): list of elements to initialize the tree with
34+
35+
Returns:
36+
None
37+
38+
>>> a = [1, 2, 3, 4, 5]
39+
>>> f1 = FenwickTree(a)
40+
>>> f2 = FenwickTree(size=len(a))
41+
>>> for index, value in enumerate(a):
42+
... f2.add(index, value)
43+
>>> f1.tree == f2.tree
44+
True
45+
"""
46+
self.size = len(arr)
47+
self.tree = deepcopy(arr)
48+
for i in range(1, self.size):
49+
j = self.next(i)
50+
if j < self.size:
51+
self.tree[j] += self.tree[i]
52+
53+
def get_array(self) -> list[int]:
54+
"""
55+
Get the Normal Array of the Fenwick tree in O(N)
56+
57+
Returns:
58+
list: Normal Array of the Fenwick tree
59+
60+
>>> a = [i for i in range(128)]
61+
>>> f = FenwickTree(a)
62+
>>> f.get_array() == a
63+
True
64+
"""
65+
arr = self.tree[:]
66+
for i in range(self.size - 1, 0, -1):
67+
j = self.next(i)
68+
if j < self.size:
69+
arr[j] -= arr[i]
70+
return arr
71+
72+
@staticmethod
73+
def next(index: int) -> int:
74+
return index + (index & (-index))
75+
76+
@staticmethod
77+
def prev(index: int) -> int:
78+
return index - (index & (-index))
79+
80+
def add(self, index: int, value: int) -> None:
81+
"""
82+
Add a value to index in O(lg N)
83+
84+
Parameters:
85+
index (int): index to add value to
86+
value (int): value to add to index
87+
88+
Returns:
89+
None
90+
91+
>>> f = FenwickTree([1, 2, 3, 4, 5])
92+
>>> f.add(0, 1)
93+
>>> f.add(1, 2)
94+
>>> f.add(2, 3)
95+
>>> f.add(3, 4)
96+
>>> f.add(4, 5)
97+
>>> f.get_array()
98+
[2, 4, 6, 8, 10]
99+
"""
100+
if index == 0:
101+
self.tree[0] += value
102+
return
103+
while index < self.size:
104+
self.tree[index] += value
105+
index = self.next(index)
106+
107+
def update(self, index: int, value: int) -> None:
108+
"""
109+
Set the value of index in O(lg N)
110+
111+
Parameters:
112+
index (int): index to set value to
113+
value (int): value to set in index
5114
6-
def update(self, i, val): # update data (adding) in index i in O(lg N)
7-
while i < self.Size:
8-
self.ft[i] += val
9-
i += i & (-i)
115+
Returns:
116+
None
10117
11-
def query(self, i): # query cumulative data from index 0 to i in O(lg N)
12-
ret = 0
13-
while i > 0:
14-
ret += self.ft[i]
15-
i -= i & (-i)
16-
return ret
118+
>>> f = FenwickTree([5, 4, 3, 2, 1])
119+
>>> f.update(0, 1)
120+
>>> f.update(1, 2)
121+
>>> f.update(2, 3)
122+
>>> f.update(3, 4)
123+
>>> f.update(4, 5)
124+
>>> f.get_array()
125+
[1, 2, 3, 4, 5]
126+
"""
127+
self.add(index, value - self.get(index))
128+
129+
def prefix(self, right: int) -> int:
130+
"""
131+
Prefix sum of all elements in [0, right) in O(lg N)
132+
133+
Parameters:
134+
right (int): right bound of the query (exclusive)
135+
136+
Returns:
137+
int: sum of all elements in [0, right)
138+
139+
>>> a = [i for i in range(128)]
140+
>>> f = FenwickTree(a)
141+
>>> res = True
142+
>>> for i in range(len(a)):
143+
... res = res and f.prefix(i) == sum(a[:i])
144+
>>> res
145+
True
146+
"""
147+
if right == 0:
148+
return 0
149+
result = self.tree[0]
150+
right -= 1 # make right inclusive
151+
while right > 0:
152+
result += self.tree[right]
153+
right = self.prev(right)
154+
return result
155+
156+
def query(self, left: int, right: int) -> int:
157+
"""
158+
Query the sum of all elements in [left, right) in O(lg N)
159+
160+
Parameters:
161+
left (int): left bound of the query (inclusive)
162+
right (int): right bound of the query (exclusive)
163+
164+
Returns:
165+
int: sum of all elements in [left, right)
166+
167+
>>> a = [i for i in range(128)]
168+
>>> f = FenwickTree(a)
169+
>>> res = True
170+
>>> for i in range(len(a)):
171+
... for j in range(i + 1, len(a)):
172+
... res = res and f.query(i, j) == sum(a[i:j])
173+
>>> res
174+
True
175+
"""
176+
return self.prefix(right) - self.prefix(left)
177+
178+
def get(self, index: int) -> int:
179+
"""
180+
Get value at index in O(lg N)
181+
182+
Parameters:
183+
index (int): index to get the value
184+
185+
Returns:
186+
int: Value of element at index
187+
188+
>>> a = [i for i in range(128)]
189+
>>> f = FenwickTree(a)
190+
>>> res = True
191+
>>> for i in range(len(a)):
192+
... res = res and f.get(i) == a[i]
193+
>>> res
194+
True
195+
"""
196+
return self.query(index, index + 1)
197+
198+
def rank_query(self, value: int) -> int:
199+
"""
200+
Find the largest index with prefix(i) <= value in O(lg N)
201+
NOTE: Requires that all values are non-negative!
202+
203+
Parameters:
204+
value (int): value to find the largest index of
205+
206+
Returns:
207+
-1: if value is smaller than all elements in prefix sum
208+
int: largest index with prefix(i) <= value
209+
210+
>>> f = FenwickTree([1, 2, 0, 3, 0, 5])
211+
>>> f.rank_query(0)
212+
-1
213+
>>> f.rank_query(2)
214+
0
215+
>>> f.rank_query(1)
216+
0
217+
>>> f.rank_query(3)
218+
2
219+
>>> f.rank_query(5)
220+
2
221+
>>> f.rank_query(6)
222+
4
223+
>>> f.rank_query(11)
224+
5
225+
"""
226+
value -= self.tree[0]
227+
if value < 0:
228+
return -1
229+
230+
j = 1 # Largest power of 2 <= size
231+
while j * 2 < self.size:
232+
j *= 2
233+
234+
i = 0
235+
236+
while j > 0:
237+
if i + j < self.size and self.tree[i + j] <= value:
238+
value -= self.tree[i + j]
239+
i += j
240+
j //= 2
241+
return i
17242

18243

19244
if __name__ == "__main__":
20-
f = FenwickTree(100)
21-
f.update(1, 20)
22-
f.update(4, 4)
23-
print(f.query(1))
24-
print(f.query(3))
25-
print(f.query(4))
26-
f.update(2, -5)
27-
print(f.query(1))
28-
print(f.query(3))
245+
import doctest
246+
247+
doctest.testmod()

0 commit comments

Comments
 (0)