Fenwick tree
range minimum query
data structures
algorithm optimization
computational efficiency

How to adapt Fenwick tree to answer range minimum queries

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

A Fenwick tree, also called a Binary Indexed Tree, is excellent for prefix sums because sums compose cleanly. Range minimum queries are different: the min operation is not invertible, so the usual "prefix query, then subtract" idea does not apply.

Why Standard Fenwick Trees Work for Sums

For sums, a Fenwick tree stores partial aggregates over carefully chosen ranges. To get a prefix sum, you combine several stored blocks. To get a range sum from left to right, you compute two prefix sums and subtract:

sum(left..right) = prefix(right) - prefix(left - 1)

That last step is what breaks for minima. If you know the minimum of 1..right and the minimum of 1..left - 1, there is no operation that reconstructs the minimum of left..right.

What a Fenwick Tree Can Do With Minimums

A Fenwick tree can be adapted for prefix minimum queries if updates only move values downward. In that limited setting, each tree node stores the minimum value seen in its covered range.

Here is a simple Python version:

python
1class PrefixMinFenwick:
2    def __init__(self, size: int):
3        self.size = size
4        self.tree = [float("inf")] * (size + 1)
5
6    def update(self, index: int, value: int) -> None:
7        while index <= self.size:
8            self.tree[index] = min(self.tree[index], value)
9            index += index & -index
10
11    def prefix_min(self, index: int) -> int:
12        result = float("inf")
13        while index > 0:
14            result = min(result, self.tree[index])
15            index -= index & -index
16        return result
17
18
19bit = PrefixMinFenwick(5)
20values = [7, 4, 9, 3, 8]
21for i, value in enumerate(values, start=1):
22    bit.update(i, value)
23
24print(bit.prefix_min(4))

Output:

text
3

This is valid for prefix minima, but it is not a full replacement for arbitrary range minimum queries.

Why Full RMQ Is Awkward in a Fenwick Tree

Suppose you want the minimum in left..right. A normal Fenwick decomposition naturally walks toward the start of the array, so it handles prefixes well. For arbitrary ranges, you would need a way to combine covered blocks without accidentally including values outside the query interval.

You also hit an update problem. With sums, changing one element lets you update ancestors by adding a difference. With minima, increasing a value may require recomputing a node from all elements in its covered range, because the old minimum might have come from the updated position.

That is why the straightforward Fenwick adaptation only works well for restricted cases such as:

  • prefix minimum queries,
  • offline processing patterns,
  • monotonic updates where values only decrease.

The Practical Answer: Use a Segment Tree

If you need true range minimum queries with arbitrary point updates, a segment tree is the standard tool. It supports both operations in O(log n) and matches the structure of the problem much better.

python
1class SegmentTreeMin:
2    def __init__(self, values: list[int]):
3        self.n = len(values)
4        self.tree = [float("inf")] * (4 * self.n)
5        self._build(values, 1, 0, self.n - 1)
6
7    def _build(self, values: list[int], node: int, left: int, right: int) -> None:
8        if left == right:
9            self.tree[node] = values[left]
10            return
11        mid = (left + right) // 2
12        self._build(values, node * 2, left, mid)
13        self._build(values, node * 2 + 1, mid + 1, right)
14        self.tree[node] = min(self.tree[node * 2], self.tree[node * 2 + 1])
15
16    def query(self, ql: int, qr: int, node: int = 1, left: int = 0, right: int | None = None) -> int:
17        if right is None:
18            right = self.n - 1
19        if ql <= left and right <= qr:
20            return self.tree[node]
21        if right < ql or qr < left:
22            return float("inf")
23        mid = (left + right) // 2
24        return min(
25            self.query(ql, qr, node * 2, left, mid),
26            self.query(ql, qr, node * 2 + 1, mid + 1, right),
27        )
28
29    def update(self, index: int, value: int, node: int = 1, left: int = 0, right: int | None = None) -> None:
30        if right is None:
31            right = self.n - 1
32        if left == right:
33            self.tree[node] = value
34            return
35        mid = (left + right) // 2
36        if index <= mid:
37            self.update(index, value, node * 2, left, mid)
38        else:
39            self.update(index, value, node * 2 + 1, mid + 1, right)
40        self.tree[node] = min(self.tree[node * 2], self.tree[node * 2 + 1])

If the original question is "how do I adapt a Fenwick tree," the honest answer is often "you usually should not, unless your query model is restricted."

Common Pitfalls

The biggest mistake is assuming that min(left..right) can be derived from two prefix minima the way sums can. It cannot.

Another issue is ignoring update semantics. A prefix-min Fenwick tree handles decreasing updates naturally, but increasing an element can leave stale minima in ancestor nodes unless you rebuild affected ranges.

Developers also spend too long forcing a BIT into a problem where a segment tree or sparse table is the simpler and more correct data structure.

Summary

  • A standard Fenwick tree is naturally suited to invertible prefix operations like sum.
  • Minimum does not support the same prefix-difference trick.
  • A Fenwick-style structure can answer prefix minimum queries under restricted update rules.
  • For arbitrary range minimum queries with updates, use a segment tree.
  • The right answer is often to change data structures, not to force an awkward adaptation.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.