Skip to main content

LeetCode Patterns

Segment Tree Pattern

A Segment Tree is a balanced binary tree built over an array where each internal node summarizes a contiguous range of the underlying data — typically a sum, min, max, gcd, or any associative function. It replaces O(n) range queries and O(n) updates with O(log n) for both, which is the standard answer whenever an interviewer combines range-aggregate queries with point updates on the same input.

By Sam K., Founder, InterviewChamp.AI · Last verified

Complexity

Time
O(log n) per query, O(log n) per update, O(n) build
Space
O(4n)

When to use Segment Tree

  • The problem mixes range queries (sum, min, max over a subarray) with point updates that change individual elements.
  • A naive prefix-sum solution would re-compute the entire prefix array on every update, giving O(n) per update.
  • The aggregate operation is associative — sum, min, max, gcd, xor — so partial results from child nodes can be combined into a parent result.
  • You need to answer many queries (thousands or more) against an array that also mutates many times.
  • The input size is in the 10^4 to 10^5 range where O(n) per query is too slow but O(log n) is comfortable.

Template

class SegmentTree {
  constructor(nums) {
    this.n = nums.length;
    this.tree = new Array(4 * this.n).fill(0);
    if (this.n > 0) this.build(nums, 1, 0, this.n - 1);
  }
  build(nums, node, start, end) {
    if (start === end) {
      this.tree[node] = nums[start];
      return;
    }
    const mid = (start + end) >> 1;
    this.build(nums, 2 * node, start, mid);
    this.build(nums, 2 * node + 1, mid + 1, end);
    this.tree[node] = this.tree[2 * node] + this.tree[2 * node + 1];
  }
  update(index, value) {
    this._update(1, 0, this.n - 1, index, value);
  }
  _update(node, start, end, index, value) {
    if (start === end) {
      this.tree[node] = value;
      return;
    }
    const mid = (start + end) >> 1;
    if (index <= mid) this._update(2 * node, start, mid, index, value);
    else this._update(2 * node + 1, mid + 1, end, index, value);
    this.tree[node] = this.tree[2 * node] + this.tree[2 * node + 1];
  }
  query(left, right) {
    return this._query(1, 0, this.n - 1, left, right);
  }
  _query(node, start, end, left, right) {
    if (right < start || end < left) return 0;
    if (left <= start && end <= right) return this.tree[node];
    const mid = (start + end) >> 1;
    return this._query(2 * node, start, mid, left, right) + this._query(2 * node + 1, mid + 1, end, left, right);
  }
}

Example LeetCode problems

#ProblemDifficultyFrequency
307Range Sum Query - Mutablemediumfoundational
315Count of Smaller Numbers After Selfhardcompany favorite
308Range Sum Query 2D - Mutablehardless common
218The Skyline Problemhardclassic
699Falling Squareshardless common
732My Calendar IIIhardfrequently asked
673Number of Longest Increasing Subsequencemediumfrequently asked
2407Longest Increasing Subsequence IIhardcompany favorite

Common mistakes

  • Off-by-one in the build recursion that puts a leaf into the wrong array slot, silently corrupting every query that touches it.
  • Allocating only 2n slots instead of 4n — the tree is balanced but not perfectly so, and the last level needs the extra headroom.
  • Forgetting to propagate the update up the tree after writing the leaf, leaving stale aggregates at every ancestor.
  • Using inclusive vs exclusive range conventions inconsistently between query and update, producing answers that are one element short or one element too long.
  • Picking a non-associative aggregate (like average or median) and getting wrong answers because parent nodes cannot be combined from child summaries.

Frequently asked questions

When should I use a Segment Tree instead of a Fenwick Tree?

Use a Segment Tree when the aggregate is min, max, gcd, or anything non-invertible, or when you need range updates with lazy propagation. Use a Fenwick Tree when the aggregate is sum or xor and the code-golf footprint matters — Fenwick is shorter, faster by a constant factor, and uses half the memory.

What is lazy propagation and when do I need it?

Lazy propagation defers a range update at an internal node until a query forces the children to be inspected. You need it whenever the problem combines range updates (add 5 to every element in [l, r]) with range queries; without it, the range update degrades to O(n log n).

Why do I need 4n nodes for a segment tree?

The tree is a perfect binary tree padded out to the next power of two, and the worst case has 2 * next_power_of_two(n) leaves. 4n is the cleanest safe bound that always fits, even for awkward sizes like n=5 or n=1000003.

Can a Segment Tree handle queries that ask for the kth element in a range?

Yes, with a merge-sort tree or a wavelet tree variant — each node stores a sorted list of its range instead of a single aggregate. The query then descends with a binary search at each level, giving O(log^2 n) per query.

Solve it now

Free. No sign-up. Python and JavaScript run instantly in your browser.

Output

Press Run or Cmd+Enter to execute

Related LeetCode patterns