segment tree Algorithm
Segment Tree is a highly efficient data structure used for multiple purposes, primarily for solving problems that involve range queries and updates on arrays. It is a binary tree that represents an array of elements, where each node in the tree stores information about a specific range of the array. The root node represents the entire array, while the leaves correspond to individual elements. Internal nodes represent a range of elements by storing aggregated information about their corresponding child nodes, which represent smaller sub-ranges. Segment trees are particularly useful in situations where the array is updated frequently and range queries need to be answered quickly, such as finding the sum or minimum value in a given range.
The construction of a segment tree is a recursive process that starts with the root node and proceeds down to the leaves. The time complexity for building a segment tree is O(n), where n is the number of elements in the array. Querying and updating the tree can be done in O(log n) time, which makes it highly efficient for large data sets. To perform a range query or update, the algorithm traverses the tree from the root, following the paths of the nodes whose ranges overlap with the target range, until it reaches the relevant leaf nodes. When updating a value in the tree, the change is propagated up the tree to adjust the aggregated information stored in the ancestor nodes. This ensures that the tree remains consistent and can continue to answer range queries accurately. Segment trees can also be extended to support other types of queries or operations, such as range multiplication, maximum value, or even custom aggregation functions.
import math
class SegmentTree:
def __init__(self, A):
self.N = len(A)
self.st = [0] * (
4 * self.N
) # approximate the overall size of segment tree with array N
self.build(1, 0, self.N - 1)
def left(self, idx):
return idx * 2
def right(self, idx):
return idx * 2 + 1
def build(self, idx, l, r): # noqa: E741
if l == r: # noqa: E741
self.st[idx] = A[l]
else:
mid = (l + r) // 2
self.build(self.left(idx), l, mid)
self.build(self.right(idx), mid + 1, r)
self.st[idx] = max(self.st[self.left(idx)], self.st[self.right(idx)])
def update(self, a, b, val):
return self.update_recursive(1, 0, self.N - 1, a - 1, b - 1, val)
def update_recursive(self, idx, l, r, a, b, val): # noqa: E741
"""
update(1, 1, N, a, b, v) for update val v to [a,b]
"""
if r < a or l > b:
return True
if l == r: # noqa: E741
self.st[idx] = val
return True
mid = (l + r) // 2
self.update_recursive(self.left(idx), l, mid, a, b, val)
self.update_recursive(self.right(idx), mid + 1, r, a, b, val)
self.st[idx] = max(self.st[self.left(idx)], self.st[self.right(idx)])
return True
def query(self, a, b):
return self.query_recursive(1, 0, self.N - 1, a - 1, b - 1)
def query_recursive(self, idx, l, r, a, b): # noqa: E741
"""
query(1, 1, N, a, b) for query max of [a,b]
"""
if r < a or l > b:
return -math.inf
if l >= a and r <= b: # noqa: E741
return self.st[idx]
mid = (l + r) // 2
q1 = self.query_recursive(self.left(idx), l, mid, a, b)
q2 = self.query_recursive(self.right(idx), mid + 1, r, a, b)
return max(q1, q2)
def showData(self):
showList = []
for i in range(1, N + 1):
showList += [self.query(i, i)]
print(showList)
if __name__ == "__main__":
A = [1, 2, -4, 7, 3, -5, 6, 11, -20, 9, 14, 15, 5, 2, -8]
N = 15
segt = SegmentTree(A)
print(segt.query(4, 6))
print(segt.query(7, 11))
print(segt.query(7, 12))
segt.update(1, 3, 111)
print(segt.query(1, 15))
segt.update(7, 8, 235)
segt.showData()