A2SV G5 - Segment Tree
A2SV G5 - Segment Tree
Tree
Lecture
Flow
Motivation Problem Variants
Definition Applications
Construction Alternatives
Operations Practice Problems
Implementation Resources
Part I
Motivation
Problem
We have an array arr[0 . . . n-1]. We should be able to
Find the sum of elements from index l to r where 0 <= l <= r <= n-1.
Change the value of a specified element of the array to a new value x.
We need to do arr[i] = x where 0 <= i <= n-1.
Definition
A Segment Tree is a data structure that stores information about array
intervals as a tree. This allows answering range queries over an array
efficiently, while still being flexible enough to allow quick modification of
the array.
Construction
We start at the bottom level (leaf nodes), and construct the tree upwards.
1 2 3 4 5 6 7 8
Construction
3 7 11 15
1 2 3 4 5 6 7 8
Construction
10 26
3 7 11 15
1 2 3 4 5 6 7 8
Construction
36
10 26
3 7 11 15
1 2 3 4 5 6 7 8
Question
Question
Question
1 2 3 4 5 6 7 8
Update
Affected Nodes
36
10 26
3 7 11 15
1 2 3 4 5 6 7 8
Update
10 26
3 7 11 15
1 2 5 4 5 6 7 8
Update
10 26
3 9 11 15
1 2 5 4 5 6 7 8
Update
12 26
3 9 11 15
1 2 5 4 5 6 7 8
Update
12 26
3 9 11 15
1 2 5 4 5 6 7 8
Query
Range Representation
38
12 26
3 9 11 15
1 2 5 4 5 6 7 8
Query
1 2 5 4 5 6 7 8
Query
Which nodes help us get the
answer easily?
38
12 26
3 9 11 15
1 2 5 4 5 6 7 8
Query
Which nodes help us get the
answer easily?
38
12 26
3 9 11 15
1 2 5 4 5 6 7 8
Query
What is the sum of values at
indices 0 to 6?
38
12 26
3 9 11 15
1 2 5 4 5 6 7 8
Query
What is the sum of values at
indices 0 to 6?
38
12 26
3 9 11 15
1 2 5 4 5 6 7 8
Question
Question
By utilizing an array, we can represent the tree implicitly, much like the
structure of a binary heap.
Simple
Implementation
To store the node values in an array, we need to handle the case where n
is not a power of 2, and have enough room in the array.
4 * n space is sufficient. Proof
Simple
Implementation
By following the binary heap structure:
2 3
4 5 6 7
8 9 10 11 12
1 2 3
13 14 15
4 5 6 7 8 9 10 11 12 13
16 17 18 19 20 21 22 23 24 25
Construction
Value For simplicity, let’s take this example where n = 16.
index
We first populate the leave nodes starting from node n.
2 3
4 5 6 7
8 9 10 11 12 13 14 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Construction
Value For the remaining nodes, from n - 1 to 1 inclusive, we’ll compute tree[i] = tree[2*i] + tree[2*i + 1].
index
136
1
36 100
2 3
10 26 42 58
4 5 6 7
3 7 11 15 19 23 27 31
8 9 10 11 12 13 14 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Iterative Implementation
The Constructor
Iterative Implementation
Tree Construction
Iterative Implementation
Tree Construction
O(n)
What’s the time complexity of
this way of tree construction?
Update
Value Let’s take this example where n = 16.
index
136
1
36 100
2 3
10 26 42 58
4 5 6 7
3 7 11 15 19 23 27 31
8 9 10 11 12 13 14 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Update
Value Updating index i in the original array, affects nodes in the path n + i, (n + i)//2, ..., 1.
index
136
1
36 100
2 3
10 26 42 58
4 5 6 7
3 7 11 15 19 23 27 31
8 9 10 11 12 13 14 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Iterative Implementation
Query
The idea behind the iterative query function is whether we should include an
element in the sum or whether we should include its parent. Consider that L
is the left border of an interval and R is the right border of the interval [L,R).
If L is odd, it means that it is the right child of its parent and our interval
includes only L and not the parent.
Query
The idea behind the iterative query function is whether we should include an
element in the sum or whether we should include its parent. Consider that L
is the left border of an interval and R is the right border of the interval [L,R).
So we will include this node to sum and move to the parent of its next
node by doing L = (L+1)/2.
If it was the left child, we would directly skip to the parent.
Query
The idea behind the iterative query function is whether we should include an
element in the sum or whether we should include its parent. Consider that L
is the left border of an interval and R is the right border of the interval [L,R).
36 100
2 3
10 26 42 58
4 5 6 7
3 7 11 15 19 23 27 31
8 9 10 11 12 13 14 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Query
Value Let’s find the sum in the range [3, 11).
Now, L = 3 + n = 3 + 16 = 19,
index
36 100
2 3
10 26 42 58
4 5 6 7
3 7 11 15 19 23 27 31
8 9 10 11 12 13 14 15
L R
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Query
Value L is odd, therefore it should be included in the sum.
index
R is odd, R - 1 should be included in the sum.
range_sum = 4 + 11
136
1
36 100
2 3
10 26 42 58
4 5 6 7
3 7 11 15 19 23 27 31
8 9 10 11 12 13 14 15
L R
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Query
Value L changes to (L + 1)//2.
index
R changes to (R-1)//2.
range_sum = 4 + 11
136
1
36 100
2 3
10 26 42 58
4 5 6 7
3 7 L 11 15 19 R 23 27 31
8 9 10 11 12 13 14 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Query
Value L is even, do nothing.
index
R is odd, R - 1 should be included in the sum.
range_sum = 4 + 11 + 19
136
1
36 100
2 3
10 26 42 58
4 5 6 7
3 7 L 11 15 19 R 23 27 31
8 9 10 11 12 13 14 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Query
Value L changes to its parent, L//2.
index
R changes to (R - 1)//2.
range_sum = 4 + 11 + 19
136
1
36 100
2 3
10 L 26 R 42 58
4 5 6 7
3 7 11 15 19 23 27 31
8 9 10 11 12 13 14 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Query
Value L is odd, it should be included in the sum.
index
R is even, do nothing.
range_sum = 4 + 11 + 19 + 26
136
1
36 100
2 3
10 L 26 R 42 58
4 5 6 7
3 7 11 15 19 23 27 31
8 9 10 11 12 13 14 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Query
Value L changes (L + 1)//2.
index
R changes to its parent, R//2.
range_sum = 4 + 11 + 19 + 26
136
1
36 L R100
2 3
10 26 42 58
4 5 6 7
3 7 11 15 19 23 27 31
8 9 10 11 12 13 14 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Query
Value Now, L is no longer less than R, We stop.
index
The variable range_sum now holds the values of the target nodes.
range_sum = 4 + 11 + 19 + 26
136
1
36 L R100
2 3
10 26 42 58
4 5 6 7
3 7 11 15 19 23 27 31
8 9 10 11 12 13 14 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Iterative Implementation
Variants
Segment trees have several variants depending on the types of
operations they support.
Point Update - Range Query
Standard Segment Tree
Practice Problems:
Codeforces | Segment Tree for the Minimum
LeetCode | Longest Increasing Subsequence II
Range Update - Point Query
(Simple Version)
This is a powerful variant where you can update an entire range and also
query a range of elements.
A lazy propagation mechanism is intensively used here.
Applications
Segment trees are widely used in scenarios requiring efficient range
queries and updates.
Operations on Segment Trees
Multidimensional Data
Persistent Data Structures
More Applications
Real-life Applications:
Range-based Statistics
Interval Scheduling
Common
Pitfalls
Off-by-One Errors: It's easy to get confused with inclusive and
exclusive bounds, when working with ranges.
Forgetting the right pointer is exclusive on the iterative
implementation.
Improper Size Allocation: The required memory size can vary
depending on the implementation strategy, which can lead to
confusion about how much memory should be allocated.
Alternatives
While segment trees are powerful for range queries and dynamic
updates, there are several alternatives with distinct advantages
depending on the problem requirements
Sparse Table
A data structure used for answering range queries (e.g., range minimum
or maximum queries) efficiently in static arrays.
Pros: Queries are answered in O(1) time after O(n logn) preprocessing.
Cons: No dynamic updates.
Fenwick Tree
(Binary Indexed Tree)
A data structure that efficiently supports prefix sum queries and point
updates.
Pros: Space-efficient and easier to implement than a segment tree.
Cons:
Range updates are more complex and typically require
modifications or using two Fenwick Trees.
More limited in functionality compared to segment trees.
Sqrt Decomposition
Aristotle
Quote of the Day
Aristotle
Thank You
2024