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 Alex Chen, 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

Practice these live with InterviewChamp.AI

Drill the Segment Tree pattern under real interview conditions with instant feedback on your reasoning, complexity claims, and code.

Practice these live with InterviewChamp.AI →