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 ("\n Original array : " , arr )
110
+
111
+ seg_tree = segment_tree_creation (arr )
112
+ print ("\n Segment 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 ("\n Range 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 ("\n Updated 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 ("\n Range sum index 1-7 = " , range_sum , "\n " )
0 commit comments