Segment Tree Data Structure - Min Max Queries
Channel: Stable Sort
Full video link:
https://www.youtube.com/watch?v=xztU7lmDLv8Data Structure • extra-hard
Segment tree supports efficient range queries with point/range updates.
It recursively partitions array intervals and stores aggregate values at internal nodes.
Use it when repeated query+update operations must both remain fast.
Typical Complexity Baseline
| Metric | Value |
|---|---|
| Query/update | O(log n) |
Prefer video learning? This explainer gives a quick visual walkthrough of the core idea before you dive into the detailed sections below.
Segment Tree Data Structure - Min Max Queries
Channel: Stable Sort
Full video link:
https://www.youtube.com/watch?v=xztU7lmDLv8Learn the core building blocks and terminology in one place before comparisons, so the mechanics are clear and duplicates are removed.
Tree node range
What it is: Interval [left,right] represented by node.
Why it matters: Tree node range is a required building block for understanding how Segment Tree stays correct and performant on large inputs.
Aggregate value
What it is: Precomputed query answer for interval.
Why it matters: Aggregate value is a required building block for understanding how Segment Tree stays correct and performant on large inputs.
Build
What it is: Construct tree bottom-up from source array.
Why it matters: Build is a required building block for understanding how Segment Tree stays correct and performant on large inputs.
Query
What it is: Combine only overlapping intervals.
Why it matters: Query is a required building block for understanding how Segment Tree stays correct and performant on large inputs.
Update
What it is: Update path from leaf to root after value change.
Why it matters: Update is a required building block for understanding how Segment Tree stays correct and performant on large inputs.
Range query
What it is: Ask aggregate over interval [l, r].
Why it matters: Ask aggregate over interval [l, r]. In Segment Tree, this definition helps you reason about correctness and complexity when inputs scale.
Point update
What it is: Change one index value and refresh affected nodes.
Why it matters: Change one index value and refresh affected nodes. In Segment Tree, this definition helps you reason about correctness and complexity when inputs scale.
Lazy propagation
What it is: Delay range updates until needed for faster bulk operations.
Why it matters: Delay range updates until needed for faster bulk operations. In Segment Tree, this definition helps you reason about correctness and complexity when inputs scale.
Associative operation
What it is: Operation where grouping does not change result.
Why it matters: Operation where grouping does not change result. In Segment Tree, this definition helps you reason about correctness and complexity when inputs scale.
Fenwick tree
What it is: Alternative structure for some prefix/range operations.
Why it matters: Alternative structure for some prefix/range operations. In Segment Tree, this definition helps you reason about correctness and complexity when inputs scale.
This walkthrough connects the core concepts of Segment Tree into one end-to-end execution flow.
Step 1
Interval [left,right] represented by node.
Before
Array
After
Transition
Why this step matters: Tree node range is a required building block for understanding how Segment Tree stays correct and performant on large inputs.
Step 2
Precomputed query answer for interval.
Before
Query range
After
Transition
Why this step matters: Aggregate value is a required building block for understanding how Segment Tree stays correct and performant on large inputs.
Step 3
Construct tree bottom-up from source array.
Before
After
Transition
Why this step matters: Build is a required building block for understanding how Segment Tree stays correct and performant on large inputs.
Step 4
Combine only overlapping intervals.
Before
Query range
After
Transition
Why this step matters: Query is a required building block for understanding how Segment Tree stays correct and performant on large inputs.
vs Prefix sums
When to choose this: Choose segment tree when updates happen frequently.
Tradeoff: Prefix sums are simpler and faster for static arrays.
vs Fenwick tree
When to choose this: Choose segment tree for broader query operation flexibility.
Tradeoff: Fenwick is simpler and often lighter for prefix sums.
vs Brute-force range scan
When to choose this: Choose segment tree for heavy query/update workloads.
Tradeoff: Brute force may be fine for tiny datasets.
Trading/finance dashboards
High-frequency metric dashboards require many range aggregates with incoming updates.
Takeaway: Segment trees provide predictable low-latency updates + queries.
Telemetry platforms
Time-bucket range aggregation can leverage tree-based interval summaries.
Takeaway: Hierarchical aggregation supports interactive exploration speed.
Gaming analytics
Live score and event windows need rapid interval stats under continuous updates.
Takeaway: Range structures reduce expensive full rescans.
Store interval sums in tree array; query and update in logarithmic time.
Complexity: Build O(n), Query O(log n), Update O(log n)
Range sum segment tree (point update)
class RangeSumSegmentTree {
private readonly sourceValues: number[]
private readonly treeValues: number[]
constructor(sourceValues: number[]) {
this.sourceValues = [...sourceValues]
this.treeValues = new Array<number>(sourceValues.length * 4).fill(0)
if (sourceValues.length > 0) {
this.buildTree({ nodeIndex: 1, leftIndex: 0, rightIndex: sourceValues.length - 1 })
}
}
queryRange(params: { queryLeftIndex: number; queryRightIndex: number }): number {
if (this.sourceValues.length === 0) return 0
return this.queryTree({ nodeIndex: 1, leftIndex: 0, rightIndex: this.sourceValues.length - 1, queryLeftIndex: params.queryLeftIndex, queryRightIndex: params.queryRightIndex })
}
updateValue(params: { updateIndex: number; nextValue: number }): void {
if (this.sourceValues.length === 0) return
this.updateTree({ nodeIndex: 1, leftIndex: 0, rightIndex: this.sourceValues.length - 1, updateIndex: params.updateIndex, nextValue: params.nextValue })
}
private buildTree(params: { nodeIndex: number; leftIndex: number; rightIndex: number }): void {
if (params.leftIndex === params.rightIndex) {
this.treeValues[params.nodeIndex] = this.sourceValues[params.leftIndex]
return
}
const middleIndex = Math.floor((params.leftIndex + params.rightIndex) / 2)
this.buildTree({ nodeIndex: params.nodeIndex * 2, leftIndex: params.leftIndex, rightIndex: middleIndex })
this.buildTree({ nodeIndex: params.nodeIndex * 2 + 1, leftIndex: middleIndex + 1, rightIndex: params.rightIndex })
this.treeValues[params.nodeIndex] = this.treeValues[params.nodeIndex * 2] + this.treeValues[params.nodeIndex * 2 + 1]
}
private queryTree(params: { nodeIndex: number; leftIndex: number; rightIndex: number; queryLeftIndex: number; queryRightIndex: number }): number {
if (params.queryRightIndex < params.leftIndex || params.rightIndex < params.queryLeftIndex) return 0
if (params.queryLeftIndex <= params.leftIndex && params.rightIndex <= params.queryRightIndex) return this.treeValues[params.nodeIndex]
const middleIndex = Math.floor((params.leftIndex + params.rightIndex) / 2)
const leftSum = this.queryTree({ nodeIndex: params.nodeIndex * 2, leftIndex: params.leftIndex, rightIndex: middleIndex, queryLeftIndex: params.queryLeftIndex, queryRightIndex: params.queryRightIndex })
const rightSum = this.queryTree({ nodeIndex: params.nodeIndex * 2 + 1, leftIndex: middleIndex + 1, rightIndex: params.rightIndex, queryLeftIndex: params.queryLeftIndex, queryRightIndex: params.queryRightIndex })
return leftSum + rightSum
}
private updateTree(params: { nodeIndex: number; leftIndex: number; rightIndex: number; updateIndex: number; nextValue: number }): void {
if (params.leftIndex === params.rightIndex) {
this.sourceValues[params.updateIndex] = params.nextValue
this.treeValues[params.nodeIndex] = params.nextValue
return
}
const middleIndex = Math.floor((params.leftIndex + params.rightIndex) / 2)
if (params.updateIndex <= middleIndex) {
this.updateTree({ nodeIndex: params.nodeIndex * 2, leftIndex: params.leftIndex, rightIndex: middleIndex, updateIndex: params.updateIndex, nextValue: params.nextValue })
} else {
this.updateTree({ nodeIndex: params.nodeIndex * 2 + 1, leftIndex: middleIndex + 1, rightIndex: params.rightIndex, updateIndex: params.updateIndex, nextValue: params.nextValue })
}
this.treeValues[params.nodeIndex] = this.treeValues[params.nodeIndex * 2] + this.treeValues[params.nodeIndex * 2 + 1]
}
}
Use these signals to decide if this data structure/algorithm is the right fit before implementation.
O(log n).O(log n) while keeping query performance stable.| # | Problem | Difficulty | Typical Complexity |
|---|---|---|---|
| 1 | Range Sum Query - Mutable | Medium | O(log n) |
| 2 | Range Sum Query 2D - Mutable | Hard | O(log n * log m) |
| 3 | Count of Smaller Numbers After Self | Hard | O(n log n) |
| 4 | My Calendar III | Hard | O(n log n) |
| 5 | Falling Squares | Hard | O(n log n) |
| 6 | Range Module | Hard | O(log n) |
| 7 | Longest Increasing Subsequence II | Hard | O(n log n) |
| 8 | Count Integers in Intervals | Hard | O(log n) |
| 9 | Rectangle Area II | Hard | Sweep + segment tree |
| 10 | Maximum Sum Queries | Hard | O(n log n) |