← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Fenwick Tree (Binary Indexed Tree)

data_structures PHP 7.4+ Advanced
debt(d7/e5/b3/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7); detection_hints.automated is 'no' and the regex pattern only hints at hand-rolled loops — bugs like 0-indexed Fenwick or non-invertible ops surface only via tests or review.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' to 'multiple files refactor' (e5); quick_fix describes swapping O(n) prefix-sum loops for a Fenwick implementation, which means introducing a new class and rewiring every update/query site within the affected component.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3); applies_to covers many contexts but a Fenwick Tree is typically encapsulated in one analytics/leaderboard component, not a system-wide architectural choice.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7); misconception treats it as interchangeable with a segment tree, and common_mistakes (0-based indexing infinite loop, attempting min/max on a non-invertible structure, n+1 sizing) contradict normal array intuitions in PHP.

About DEBT scoring →

Also Known As

binary indexed tree BIT Fenwick BIT

TL;DR

A compact array-backed structure giving O(log n) prefix-sum queries and O(log n) point updates with minimal code and memory overhead.

Explanation

A Fenwick Tree, also called a Binary Indexed Tree (BIT), is a 1-indexed array where each cell stores the sum of a power-of-two-sized range of original values. The size of that range is determined by the lowest set bit of the index, so traversal up or down the implicit tree is done with simple bit tricks: `i += i & -i` to move to the next responsible parent during updates, and `i -= i & -i` to walk prefix segments during queries. Both operations are O(log n), and the structure uses exactly n+1 array slots with no node objects or pointers. Fenwick Trees solve the classic running-totals problem: given an array that mutates over time, answer 'what is the sum of elements 1..k?' or 'what is the sum of elements l..r?' (via two prefix queries) without rescanning. Compared to a Segment Tree, Fenwick is roughly half the code, half the memory, and slightly faster in practice for pure prefix-sum workloads, at the cost of being harder to extend to arbitrary range queries like min/max. Common applications include real-time leaderboard scoring, inversion counting in merge-sort-style algorithms, cumulative frequency tables for statistics, and order-book or analytics rollups where individual cells change frequently and dashboards need fast aggregates. The structure can be generalised: 2D Fenwick Trees support O(log n * log m) updates on a grid, and range-update/range-query variants exist using two parallel BITs. For databases, you would typically use MySQL window functions for one-off rollups, but a Fenwick Tree wins when updates and queries are interleaved at high frequency in application memory.

Common Misconception

A Fenwick Tree is just a slower segment tree, so always reach for a segment tree. In reality Fenwick is smaller, faster, and easier to write when you only need prefix sums or invertible aggregates.

Why It Matters

When a dataset has frequent point updates and frequent range-sum queries, naive recomputation is O(n) per query and tanks dashboards or leaderboards under load; Fenwick Trees turn both operations into O(log n), making real-time analytics feasible.

Common Mistakes

  • Using 0-based indexing internally — the `i & -i` trick requires 1-based indices, and a zero index loops forever.
  • Trying to support non-invertible operations like min or max — Fenwick relies on subtraction to derive range queries, so use a segment tree for those.
  • Forgetting to size the array as n+1, causing off-by-one overflows at the last index.
  • Rebuilding the tree on every update instead of using the O(log n) update path.
  • Computing range sums as a single walk instead of `prefix(r) - prefix(l-1)`.

Code Examples

✗ Vulnerable
<?php
// Naive: O(n) per range-sum query, O(1) per update.
// Fine for a few hundred rows; collapses on hot leaderboards.
class LeaderboardNaive {
    private array $scores; // 1-indexed
    private int $n;

    public function __construct(int $n) {
        $this->n = $n;
        $this->scores = array_fill(0, $n + 1, 0);
    }

    public function update(int $userId, int $delta): void {
        $this->scores[$userId] += $delta;
    }

    // O(n) — walks the whole prefix every call.
    public function topRankCumulative(int $rank): int {
        $sum = 0;
        for ($i = 1; $i <= $rank; $i++) {
            $sum += $this->scores[$i];
        }
        return $sum;
    }
}

// MySQL equivalent that re-scans on every dashboard refresh:
// SELECT SUM(score) FROM leaderboard WHERE rank <= ?;
// Each call is O(n) in the index range — no incremental structure.
✓ Fixed
<?php
// Fenwick Tree: O(log n) update, O(log n) prefix query.
final class FenwickTree {
    private array $tree;
    private int $n;

    public function __construct(int $n) {
        $this->n = $n;
        $this->tree = array_fill(0, $n + 1, 0); // 1-indexed
    }

    public function update(int $i, int $delta): void {
        for (; $i <= $this->n; $i += $i & -$i) {
            $this->tree[$i] += $delta;
        }
    }

    public function prefixSum(int $i): int {
        $sum = 0;
        for (; $i > 0; $i -= $i & -$i) {
            $sum += $this->tree[$i];
        }
        return $sum;
    }

    public function rangeSum(int $l, int $r): int {
        return $this->prefixSum($r) - $this->prefixSum($l - 1);
    }
}

// Usage: leaderboard cumulative scores in-memory, mirrored from MySQL.
// CREATE TABLE leaderboard (user_id INT PRIMARY KEY, score INT);
// On score change: $bit->update($userId, $delta) plus single-row UPDATE.
// Dashboard 'sum of top K ranks' is now O(log n) instead of O(n).

Added 18 May 2026
Views 48
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 6 pings T 2 pings F 1 ping S 2 pings S 0 pings M 1 ping T 1 ping W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 1 ping T 0 pings W
No pings yet today
Google 6 Perplexity 6 Meta AI 2 SEMrush 2 Ahrefs 1
crawler 15 crawler_json 2
DEV INTEL Tools & Severity
🟢 Low ⚙ Fix effort: High
⚡ Quick Fix
Replace O(n) prefix-sum loops over mutating arrays with a Fenwick Tree using `i += i & -i` for updates and `i -= i & -i` for queries on a 1-indexed array.
📦 Applies To
PHP 7.4+ web cli queue-worker library
🔗 Prerequisites
🔍 Detection Hints
for\s*\(.*\$i\s*<=\s*\$rank.*\)\s*\{[^}]*\+=\s*\$(scores|counts|freq)
Auto-detectable: ✗ No
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: High Context: Function Tests: Regenerate

✓ schema.org compliant