Skip to content

Commit

Permalink
fenwick_tree.py: Add FenwickTree Data Structure
Browse files Browse the repository at this point in the history
This adds Fenwick Data Structure which can update an element of a list
and query the sum of any range of it in only O(log(N)) time complexity.

Closes NITSkmOS#220
  • Loading branch information
SnowyCoder authored and sangamcse committed Oct 19, 2018
1 parent 44d7f53 commit 21265c7
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ This repository contains examples of various algorithms written on different pro
| [Linear Linked List](https://en.wikipedia.org/wiki/Linked_list) | [:octocat:](linked_list/C) | [:octocat:](linked_list/Cpp) | | [:octocat:](linked_list/Python) |
| [AVL Tree](https://en.wikipedia.org/wiki/AVL_tree) | [:octocat:](avl_tree/C) | | [:octocat:](avl_tree/Java) | [:octocat:](avl_tree/Python) |
| [Binary Search Tree](https://en.wikipedia.org/wiki/Binary_search_tree) | | [:octocat:](binary_search_tree/Cpp) | | |
| [Fenwick Tree](https://en.wikipedia.org/wiki/Fenwick_tree) | | | [:octocat:](fenwick_tree/java) | |
| [Fenwick Tree](https://en.wikipedia.org/wiki/Fenwick_tree) | | | [:octocat:](fenwick_tree/java) | [:octocat:](fenwick_tree/Python) |


## Sample Run
Expand Down
90 changes: 90 additions & 0 deletions fenwick_tree/Python/fenwick_tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
def lsb(x):
"""Returns the Last Significant Bit (LSB) of x"""
return x & -x


class FenwickTree:
"""A simple implementation of a Fenwick tree"""

def __init__(self, size):
self.data = [0] * (size + 1) # Initialise 0-filled data array

def _cumulative_sum(self, right):
"""
Returns the sum from 0 to right (inclusive)
:param right: The inclusive right index
:return: sum([left; right])
"""
res = 0
index = right + 1 # Convert to 1-based index
while index > 0:
res += self.data[index]
index -= lsb(index)

return res

def sum(self, left, right):
"""
Returns the sum from left (inclusive) to right (inclusive)
:param right: The inclusive right index
:param left: The inclusive left index
:return: sum([left; right])
"""
return self._cumulative_sum(right) - self._cumulative_sum(left - 1)

def add(self, index, diff):
"""
Add the difference to the data index
:param index: The index to update
:param diff: The amount to add to that index
"""
index += 1 # Convert to 1-based index

while index < len(self.data):
self.data[index] += diff
index += lsb(index)


def main():
tree = FenwickTree(8)

# Set initial value(by default it's 0)
for i in range(8):
tree.add(i, i)

# Now it's 0, 1, 2, 3, 4, 5, 6, 7

# [0, 1, 2, 3, 4, 5, 6, 7]
# ^--------------------^
# 0 + 1 + 2...+ 7 = 28
assert tree.sum(0, 7) == 28

# [0, 1, 2, 3, 4, 5, 6, 7]
# ^-----^
# 4 + 5 + 6 = 15
assert tree.sum(4, 6) == 15

# [0, 1, 2, 3, 4, 5, 6, 7]
# ^--------^
# 0 + 1 + 2 + 3 = 6
assert tree.sum(0, 3) == 6

# Update the numbers again
for i in range(8):
tree.add(i, 1 - i)
# Initial value i
# Added: 1 - i
# Res: i + 1 - i = 1

# [1, 1, 1, 1, 1, 1, 1, 1]
# ^--------------------^
# 1 * 8 = 8
assert tree.sum(0, 7) == 8
print("Tests passed")


if __name__ == "__main__":
main()

0 comments on commit 21265c7

Please sign in to comment.