Data Structures/Fenwick & Segment Trees

Lesson 4.71,455 words

Fenwick & Segment Trees

A prefix-sum array answers a range sum in O(1)O(1) but pays O(n)O(n) per update; a plain array updates in O(1)O(1) but pays O(n)O(n) per range sum. Fenwick and segment trees give us both in O(logn)O(\log n).

╌╌╌╌

We have an array and two operations we want to interleave freely: update a single entry, and ask for the sum of a contiguous range . The two obvious data structures each ace one operation and fail the other. Keep as is and an update is a single write in , but a range sum scans the range in . Precompute a prefix-sum array and a range sum collapses to in , but now a single update to disturbs every with , an repair. We want a structure that splits the difference and does both in .

The tradeoff that motivates these structures. A range sum on a plain array must scan the whole range, (top). A prefix-sum array answers that sum as one subtraction , but a single update to then dirties every later prefix , to repair (bottom). Each structure is on one operation and on the other.

The idea, in the spirit of the previous lessons on augmenting trees with subtree summaries,1 is to store partial sums over blocks so that any prefix is the sum of a few blocks and any single element lives in only a few blocks. Two classic structures realize this: the Fenwick tree, which is remarkably compact and exploits the binary representation of the index, and the segment tree, which is more general and handles any associative aggregate plus range updates.

Fenwick trees: indexing by the low bit

A Fenwick tree (or binary indexed tree) is a 1-indexed array where stores the sum of a contiguous block of ending at index . The length of that block is , the value of the lowest set bit of :

That is, covers the half-open range . The trick rides on two's-complement arithmetic: flips every bit of and adds one, so isolates exactly the lowest set bit. If then , so covers indices ; if then , so covers the whole prefix .

Each covers the block of length ending at

Prefix sum. To compute we peel off blocks from the right. accounts for the topmost block ending at ; the rest of the prefix ends at , so we jump there and repeat, clearing one set bit each step until we reach .

Algorithm:PrefixSum(F,i)\textsc{PrefixSum}(F, i)return A[1]++A[i]A[1] + \dots + A[i]
  1. 1
    s0s \gets 0
  2. 2
    while i>0i > 0 do
  3. 3
    ss+F[i]s \gets s + F[i]
  4. 4
    iilowbit(i)i \gets i - \operatorname{lowbit}(i)
    clear the lowest set bit
  5. 5
    return ss

Point update. When changes by , every whose block contains must change by . Those are exactly the indices reached by repeatedly adding the low bit: starting at , each step moves to the next larger block that covers , until we run past .

Algorithm:Update(F,k,δ)\textsc{Update}(F, k, \delta) — add δ\delta to A[k]A[k]
  1. 1
    while knk \le n do
  2. 2
    F[k]F[k]+δF[k] \gets F[k] + \delta
  3. 3
    kk+lowbit(k)k \gets k + \operatorname{lowbit}(k)
    move to the next covering block

The two walks are mirror images on the same number line. climbs to larger indices by adding the low bit, visiting every block that owns the changed element; descends to smaller indices by subtracting it, peeling off the blocks that tile the prefix. The same hop drives both, in opposite directions.

The lowbit duality. climbs adding the low bit; descends clearing it

A range sum is then two prefix queries:

so both update and range sum cost , with occupying a single array of words and no pointers.2 Building takes by a linear in-place pass (add each into its parent ) rather than separate updates.

The one catch: this works because sums are invertible — we recover by subtracting two prefixes. For non-invertible aggregates like or , is meaningless, and we need a structure that queries an arbitrary range directly. That is the segment tree.

Segment trees: a balanced tree of canonical ranges

A segment tree over is a balanced binary tree whose leaves are the array entries and whose every internal node stores the aggregate of the contiguous range its subtree spans. The root covers ; a node covering with splits at into children covering and . The stored aggregate can be sum, , , or : any associative operation (formally, any monoid), since a node's value is its two children's values combined.

A segment tree over 8 elements; the canonical nodes covering are highlighted

Query. To aggregate we descend from the root. At a node covering : if lies entirely inside we return its stored value without recursing (a canonical node); if it is disjoint from we return the monoid identity; otherwise we recurse into both children and combine. The query range decomposes into canonical nodes, at most two per level of the tree, so a range query costs . In the figure, is covered by the four shaded nodes , , , and their union is exactly .

Point update. To change , update the corresponding leaf and walk back up to the root, recomputing each ancestor as the combination of its (now-updated) children — one node per level, work. Building the tree bottom-up visits each of the nodes once, so construction is , and the tree needs at most (commonly allocated as ) nodes, roughly to a Fenwick tree's memory.

Lazy propagation: range updates in

A point-update segment tree still pays to add a value to a whole range element by element. Lazy propagation fixes this. When an update applies to a range that exactly covers a node's interval, we apply it to that node's aggregate and stash a pending tag on the node instead of recursing into its children. The tag is pushed down to the children only later, lazily, when a subsequent query or update actually needs to enter that subtree.

Lazy push-down. A pending tag on is applied to that node's aggregate; only when a query descends does the tag flow to the children and

With lazy tags both range update and range query run in . This is the segment tree's decisive advantage over the Fenwick tree: it supports non-invertible aggregates (, ) and whole-range modifications, at the cost of more memory and a more involved implementation.3

Choosing between them

Both give point-update and range-query; the choice is about generality versus footprint.

  • Fenwick tree. Pick it when the aggregate is an invertible group operation (sum, xor) and you only need point updates. It is a single array, cache-friendly, a dozen lines of code, and the constant factors are tiny. Range sum is .
  • Segment tree. Pick it when you need or any non-invertible aggregate, or range updates via lazy propagation. It is strictly more general, and you pay for it with to the memory and a more involved implementation.4

A useful slogan: Fenwick is the specialist, the segment tree is the generalist. When you reach for range-sum-query-mutable, a Fenwick tree is the crisp answer; when the skyline or a range-assign problem demands over a mutable range, the segment tree with lazy propagation is the tool.

Takeaways

  • A static prefix-sum array answers range sums in but updates in ; a plain array updates in but sums in . Fenwick and segment trees achieve both in .
  • A Fenwick tree is a 1-indexed array where holds the sum of the block , with . Prefix sum walks down clearing low bits; update walks up adding low bits, each .
  • Fenwick range sum relies on invertibility: . It fails for .
  • A segment tree stores each node's range aggregate (any associative op). Build , point update , and range query by decomposing into canonical nodes.
  • Lazy propagation defers a range update by tagging canonical nodes and pushing tags down only when needed, giving range update + range query.
  • Fenwick = tiny, fast, sum-like invertible aggregates; segment tree = general and lazy range ops, at to the memory.

Footnotes

  1. CLRS, Ch. 14, Augmenting Data Structures (§14.2): attach summary fields to nodes and maintain them through updates, the general recipe both structures specialize.
  2. Skiena, §3.x, Range Queries / Augmented Structures: the binary indexed tree as a minimal-overhead structure for dynamic prefix sums.
  3. Erickson, Ch., Data Structures: segment trees over canonical ranges, range decomposition, and lazy propagation for range updates.
  4. Sedgewick, §, Binary Indexed & Segment Trees: practical comparison of the binary indexed tree against the more general segment tree.
Practice

╌╌ END ╌╌