Skip to content

Commit 4ca216e

Browse files
authored
Merge pull request #570 from pushpit-J19/segment-tree
Segment Tree
2 parents 5a44bf2 + c44d34f commit 4ca216e

File tree

1 file changed

+122
-0
lines changed

1 file changed

+122
-0
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"""
2+
Contains program from Segment tree creation from an array, range sum query and update query for the same.
3+
4+
5+
Segment trees are special kind of trees that allows answering range queries and updating values in an array effectively (both in O(logn)),
6+
For an array, its segment tree will have as leaf nodes = the elements of the array,
7+
and its internal nodes store the value of a preprocessed value, like the sum of all its children
8+
9+
eg arr : [1,2,3,4]
10+
parent node of (1,2) = 3, parent node of (3,4) = 7
11+
parent node of (3,7) = 10 (this will also be the root node)
12+
Equivalent segment tree : [0, 10, 3, 7, 1, 2, 3, 4]
13+
where 0 has been added as first element to ease the calculations
14+
15+
16+
Sum range query is the sum of all elements in an array over a given range (l, r) both included, where l and r are the indicies.
17+
eg for arr = [1,2,3,4], sum_range(0,2) = 1+2+3 = 6
18+
The above approach has time complexity : O(n)
19+
It can be improved by using the segment trees.
20+
21+
For sum range query, the internal nodes store the sum of their children nodes.
22+
With segment trees, instead of going to every element, we already have the sum calculated in the parent node, so we just need to get that.
23+
to get above example of sum_range(0,2) = 1+2+3 :-
24+
tree : [0, 10, 3, 7, 1, 2, 3, 4]
25+
index: [0, 1, 2, 3, 4, 5, 6, 7]
26+
l = 4, r = 6 are the tree indices
27+
elem 1 and 2 have a common parent, where elem 3 has no common parent with others, so we add tree[r] = 3 to range_sum and shift r left,
28+
that is, we move r to left if it is even. Similarly, l will have to be moved forward if it is odd.
29+
now l = 4, r = 5, range_sum=3, and we move to parent of both l and r
30+
l = 2, r = 2, now r is even, so tree[r]=tree[2]=3 is added to range_sum. range_sum = 3+3 = 6, r = 1
31+
so we stop, range_sum is our answer
32+
33+
34+
On updating, say changing 1 -> -1 on index 0
35+
arr becomes = [-1, 2, 3, 4]
36+
37+
we need to only update the parent nodes (which are at index/2 of a node). So, no of queries = height of tree O(log(n)),
38+
instead of updating boxes in sqrt decomposition O(sqrt(n)), and all the sums in dp approach O(n)
39+
so updated tree = [0, 8, 1, 7, -1, 2, 3, 4]
40+
41+
"""
42+
43+
from math import ceil, log2
44+
45+
46+
def segment_tree_creation (arr):
47+
"""The function creates a segment tree in O(n) time
48+
arguments : array, whose corresponding segment tree is to be made
49+
returns : the correspoding segment tree in a list format
50+
"""
51+
n = len(arr)
52+
53+
# for n leaf nodes, there are maximum double of 2**max_height-1 nodes, adding extra 0th elem for ease of calculations
54+
height = ceil(log2(n))
55+
m = 2*(2**height)
56+
tree = [0]*m
57+
58+
# filling the second half of the tree list (leaf nodes) with the array elements
59+
for i in range(n):
60+
index = i+int(m/2)
61+
tree[index] = arr[i]
62+
63+
# creating the internal nodes, calculating the from leaf to the root
64+
for i in range(int(m/2)-1,0,-1):
65+
tree[i] = tree[2*i] + tree[2*i+1]
66+
return tree
67+
68+
69+
def segment_tree_range_sum_query (tree, l, r):
70+
"""The function tells the sum of elements in a given range using the segment tree in O(log(n))
71+
input : segment tree, array starting and ending index of range
72+
returns the sum of elements of array in that range"""
73+
74+
m = len(tree)
75+
l,r = l+m//2, r+m//2 # converting array index to segment tree index
76+
rsum = 0
77+
while (l<=r) : # while the lower and upper limit do not cross each other
78+
if l%2 == 1: # if lower range index is a right child, add that and jump to next index (towards right)
79+
rsum += tree[l]
80+
l += 1
81+
if r%2 == 0: # if upper range index is a left child, add that and jump to next index (towards left)
82+
rsum += tree[r]
83+
r -= 1
84+
85+
l, r = l//2, r//2 # both the indices are have a common ancestor in tree, so move up, till that ancestor
86+
87+
return rsum
88+
89+
90+
def segment_tree_update_query (arr, tree, index, value):
91+
"""The function updates the node in array (leaf node) and all its parent nodes till root node, complexity = O(log(n))
92+
input: array to be updated, tree to be updated, index of array, new value
93+
returns : updated array and the tree
94+
"""
95+
arr[index] = value
96+
m = len(tree)
97+
i = m//2 + index # getting the index according to the tree from array index
98+
change = value - tree[i]
99+
while i>0 :
100+
tree[i] += change # updating the current nodes
101+
i = i//2 # going to update the parent
102+
return arr, tree
103+
104+
105+
# DRIVER CODE
106+
if __name__ == '__main__':
107+
108+
arr = [5, 8, 6, 3, 2, 7, 4, 6]
109+
print("\nOriginal array : ", arr)
110+
111+
seg_tree = segment_tree_creation(arr)
112+
print("\nSegment tree : ", seg_tree)
113+
114+
range_sum = segment_tree_range_sum_query(seg_tree, 1, 7) # 8 + 6 + 3 + 2 + 7 + 4 + 6 = 36
115+
print("\nRange sum index 1-7 = ", range_sum)
116+
117+
arr, seg_tree = segment_tree_update_query(arr, seg_tree, 2, -5) # changing to 6 -> -5 at index 2
118+
print("\nUpdated array tree : ", arr)
119+
print("Updated Segment tree : ", seg_tree)
120+
121+
range_sum = segment_tree_range_sum_query(seg_tree, 1, 7) # 8 + (-5) + 3 + 2 + 7 + 4 + 6 = 25
122+
print("\nRange sum index 1-7 = ", range_sum, "\n")

0 commit comments

Comments
 (0)