Hi👋SpeedAlgo • Deliberate Practice & Cognitive Ergonomics for Software Engineers

An interactive algorithm mastery and technical interview preparation platform published by Hi👋WebEnterprise. Built for senior and staff software engineers preparing for rigorous coding screens at top tech companies (FAANG/MAMAA).

13 Core Algorithmic Patterns & 225 Practice Problems

  • 1. Two Pointers (16 Paradigms, 43 Problems): Opposite ends, sorted pair sums, palindrome verification, trapping rain water, 3Sum, container with most water — includes the Sliding Window and Fast & Slow Pointers paradigms (contiguous subarray invariants, longest substrings, Floyd's cycle detection, linked list middle).
  • 2. Binary Search (9 Paradigms, 14 Problems): Monotonic predicate partitioning (feasible(x)), boundary search, rotated sorted arrays, square roots, minimum ship capacity, median of two sorted arrays, matrix median on value range.
  • 3. Bit Manipulation (5 Paradigms, 11 Problems): Counting set bits, XOR cancellation, bit reversal, bitmask pairing, maximum XOR with a bitwise trie.
  • 4. Math & Geometry (8 Paradigms, 14 Problems): Sieve of Eratosthenes, integer/roman conversions, modular arithmetic, geometric simulation.
  • 5. Tree/Graph Depth-First Search (15 Paradigms, 24 Problems): Path sums, lowest common ancestor, tree diameter, validating BSTs, tree DP (House Robber III, Binary Tree Cameras).
  • 6. Tree/Graph Breadth-First Search (5 Paradigms, 12 Problems): Level-order traversals, shortest path in unweighted graphs, zig-zag traversals, rotting oranges, word ladders.
  • 7. Graphs (12 Paradigms, 22 Problems): Topological sort, cycle detection, Dijkstra's algorithm, bipartite graph validation, network delay.
  • 8. Backtracking (6 Paradigms, 14 Problems): Subsets, permutations, combinations, constraint satisfaction, pruning, N-Queens, Sudoku solver.
  • 9. Dynamic Programming (13 Paradigms, 28 Problems): Memoization vs tabulation, 0/1 knapsack, unbounded knapsack, coin change, edit distance, longest common subsequence.
  • 10. Heap / Priority Queue (9 Paradigms, 11 Problems): Running medians, top-k elements, interval scheduling, IPO, k-way merges.
  • 11. Advanced Data Structures (7 Paradigms, 17 Problems): Trie (prefix tree), Union-Find (Disjoint Set Union), LRU Cache, LFU Cache, range-sum queries with updates.
  • 12. Stack (5 Paradigms, 9 Problems): Next greater element, largest rectangle in histogram, remove k digits, decode string, min stack.
  • 13. Intervals & Greedy (2 Paradigms, 6 Problems): Merge intervals, interval scheduling, meeting rooms by sweep line, jump game.

4-Stage Deliberate Practice Framework

  1. Stage 1 (Compare & Learn): Read the pattern's mental model, loop invariant and Big-O proof, compare the pattern's template with the problem's solution side by side, and use the step-by-step debugger. The solution can also be shown in C#, Java, TypeScript, C++, Go or Rust for almost every problem.
  2. Stage 2 (Active Recall): Rebuild the solution from memory in a code editor, with an invariant checklist and optional progressive hints, and check it against a judged test suite.
  3. Stage 3 (AI Interview Coach, coming soon): A mock-interview stage still in development; for now, students go straight from Stage 2 to Stage 4.
  4. Stage 4 (Solve on Your Own): Solve a new problem on your own against a 15-minute timer; your code is graded against a test suite. Python and TypeScript run in your browser and C# runs on our server (design problems grade in Python and TypeScript).

Equipped with a spaced-repetition review hub on a fixed 7-step schedule (1 day up to 90 days), a two-pane Studio workspace, and study notes.

Pricing, Access & Commercial Terms

  • Core Curriculum: 100% Free. No credit card required.
  • Coins: New accounts get 40 free coins once, the daily claim adds +20 coins once a day, and inviting a friend earns you +25 coins when they complete their first practice step. These are the only ways to earn coins.
  • AI Coach: Each AI coach question costs 1-5 coins, priced by the server according to its size.
  • BYOK (Bring Your Own Key): AI coaching is free with your own Google Gemini, OpenAI or OpenRouter API key (BYOK).
  • Extras: Coins also unlock extras for 24 hours: a Recall hint level (1 coin), a Solve solution reveal (2), a LeetCode problem card's solutions (1) and a pattern's extra canonical problems (1).
  • Payments & Refunds: Nothing on the site is sold for money: coins can only be earned, never bought. There are no subscriptions or charges, so there is nothing to refund.
  • Platform Operator: Hi👋WebEnterprise. Support & policies at hispeedalgo.com.
Skip to main content
Hi👋SpeedAlgo

Invariant-First Algorithmic Mastery

Arena
238Items
Theory Context•Advanced Data Structures
MediumLC 307

Range Sum Query - Mutable (LeetCode 307)

Target Frequency:GoogleAmazonMeta

You get an integer array nums, followed by a mix of two kinds of requests in any order: change one element, or report the sum of a contiguous stretch of elements.

Build the NumArray class:

  • NumArray(nums) stores the array.
  • update(index, val) sets nums[index] to val.
  • sumRange(left, right) returns nums[left] + nums[left + 1] + ... + nums[right], both ends included (left <= right).

Worked Examples

Example 1
Input:["NumArray", "sumRange", "update", "sumRange"] [[[1, 3, 5]], [0, 2], [1, 2], [0, 2]]
Output:[null, 9, null, 8]
103152update(1, 2)
Explanation: The array starts as [1, 3, 5], so sumRange(0, 2) is 1 + 3 + 5 = 9. update(1, 2) makes it [1, 2, 5], and the next sumRange(0, 2) is 1 + 2 + 5 = 8.

⚖️Formal Constraints & Bounds

  • 1 <= nums.length <= 3 * 104

  • -100 <= nums[i] <= 100

  • 0 <= index < nums.length

  • -100 <= val <= 100

  • 0 <= left <= right < nums.length

  • At most 3 * 104 calls will be made to update and sumRange.

Deep-Dive & Conceptual Insights

Core Insight

Store sums of blocks whose length is the index's lowest set bit: a prefix is the sum of O(log N) disjoint blocks, and a changed position belongs to O(log N) blocks, so both operations walk one block per bit.

Real-World Scenario & Production Applications

Live dashboards and leaderboards that keep running totals while individual values keep changing: bucketed counters (requests per second, sales per day) where a single bucket is corrected and a range total is read on every page load. A plain prefix array would need an O(N) rebuild after every change.

Step-by-Step Execution Trace Table

nums = [1, 3, 5] (n = 3). Each value is added at i = index + 1; i & (-i) decides the jump.

CallWalk of itree[1..3] afterReturns
build: update(1, 1)1 → 2 → 4 (stop, > 3)[1, 1, 0]
build: update(2, 3)2 → 4 (stop)[1, 4, 0]
build: update(3, 5)3 → 4 (stop)[1, 4, 5]
sumRange(0, 2) = query(3) - query(0)query(3): tree[3] + tree[2] = 5 + 4 (3 → 2 → 0)[1, 4, 5]9
update(1, 2): add 2 - 3 = -1 at i = 22 → 4 (stop)[1, 3, 5]null
sumRange(0, 2)query(3): 5 + 3 = 8[1, 3, 5]8
Worked on Paper

The Math, Step by Step

1. The range each tree entry covers

The Fenwick tree numbers the array positions from 1, so position index of nums is i=index+1i = \text{index} + 1i=index+1. Write low(i)\text{low}(i)low(i) for the value of the lowest 1 bit of iii. For example, 666 is 110 in binary, so low(6)=2\text{low}(6) = 2low(6)=2.

Entry tree[i] holds the sum of low(i)\text{low}(i)low(i) values: the positions from i−low(i)+1i - \text{low}(i) + 1i−low(i)+1 to iii. Written as a half-open range, it covers (i−low(i), i](i - \text{low}(i),\ i](i−low(i), i].

iiiiii in binarylow(i)\text{low}(i)low(i) = i & -itree[i] covers
100011(0,1](0, 1](0,1]
200102(0,2](0, 2](0,2]
300111(2,3](2, 3](2,3]
401004(0,4](0, 4](0,4]
501011(4,5](4, 5](4,5]
601102(4,6](4, 6](4,6]
701111(6,7](6, 7](6,7]
810008(0,8](0, 8](0,8]
2. Why i & -i is the lowest 1 bit

In two's complement, −i-i−i is computed as "flip every bit of iii, then add 1". Say iii ends with a 1 followed by kkk zeros. Flipping gives a 0 followed by kkk ones. Adding 1 turns those kkk ones back into zeros and the 0 into a 1. So −i-i−i has the same lowest 1 bit as iii, with the same kkk zeros after it. Every bit to the left of it is flipped.

& keeps a bit only where both numbers have a 1. Only the lowest 1 bit is 1 in both. So i & -i equals low(i)\text{low}(i)low(i). For i=6i = 6i=6: in 8 bits, 666 is 00000110 and −6-6−6 is 11111010, and their & is 00000010, which is 2.

3. Why a query and an update each touch about log⁡n\log nlogn entries

A prefix sum walks down. query(i) adds tree[i], which covers (i−low(i),i](i - \text{low}(i), i](i−low(i),i]. Then it sets i←i−low(i)i \leftarrow i - \text{low}(i)i←i−low(i) and repeats. The next range ends exactly where the last one started. So the ranges fit together without gaps or overlaps, and they cover (0,i](0, i](0,i]. Each step clears the lowest 1 bit of iii, so the loop runs once per 1 bit, at most log⁡2n+1\log_2 n + 1log2​n+1 times. For i=7i = 7i=7 the ranges are (6,7](6, 7](6,7], (4,6](4, 6](4,6] and (0,4](0, 4](0,4].

An update walks up. A change at position iii must reach every entry whose range contains iii. update(i, delta) adds delta to tree[i], then sets i←i+low(i)i \leftarrow i + \text{low}(i)i←i+low(i) and repeats. Adding the lowest 1 bit moves to the next larger entry whose range still reaches down to position iii. Each step makes the lowest 1 bit at least twice as large, so the loop runs at most log⁡2n+1\log_2 n + 1log2​n+1 times. With n=8n = 8n=8, a change at position 3 visits entries 3, 4 and 8. The table of part 1 shows that these are exactly the entries that cover position 3.

A range sum is the difference of two prefix sums, as for a fixed array:

sum(left…right)=query(right)−query(left−1)\text{sum}(\text{left} \ldots \text{right}) = \text{query}(\text{right}) - \text{query}(\text{left} - 1)sum(left…right)=query(right)−query(left−1)

with 1-based positions. sumRange adds 1 to both indices first.

4. The 1-based positions and the size of the change

Position 0 does not work in a Fenwick tree. low(0)=0\text{low}(0) = 0low(0)=0, so update(0, ...) would add 0 to iii forever. That is why the code stores position index at i=index+1i = \text{index} + 1i=index+1, and why tree has n+1n + 1n+1 entries with tree[0] unused. For query(0), the loop while i > 0 runs zero times and returns 0, which is the sum of no elements.

The tree stores sums, so it must receive the change of a value, not the new value. NumArray.update sends val - self.nums[index], and then stores val in self.nums for the next change. Sending val itself would add the new value on top of the old one.

5. Example 1 by hand

nums = [1, 3, 5], so n=3n = 3n=3. The constructor calls update once per value:

CallEntries visitedtree[1..3] after
update(1, 1)1, then 1+1=21 + 1 = 21+1=2[1,1,0][1, 1, 0][1,1,0]
update(2, 3)2 (next would be 4, past nnn)[1,4,0][1, 4, 0][1,4,0]
update(3, 5)3 (next would be 4, past nnn)[1,4,5][1, 4, 5][1,4,5]

So tree[2] = 4 holds positions 1 and 2, and tree[3] = 5 holds position 3 alone.

sumRange(0, 2) computes query(3) - query(0). query(3) adds tree[3] = 5, moves to 3−1=23 - 1 = 23−1=2, adds tree[2] = 4, and moves to 2−2=02 - 2 = 02−2=0. It returns 999. query(0) returns 0. The answer is 999, the expected output.

update(1, 2) changes nums[1] from 3 to 2, so it sends the change 2−3=−12 - 3 = -12−3=−1 to position 2. Entry 2 becomes 4−1=34 - 1 = 34−1=3. The next entry would be 2+2=42 + 2 = 42+2=4, past n=3n = 3n=3, so the update stops.

The second sumRange(0, 2) adds tree[3] = 5 and tree[2] = 3, and returns 888, the expected output.

6. From the math to the code
MathIn the code
positions 1…n1 \ldots n1…n, tree[0] unusedself.tree = [0] * (n + 1) and index + 1
low(i)\text{low}(i)low(i)i & (-i)
walk up to every entry that covers iiiwhile i <= self.n: with i += i & (-i)
walk down over ranges that tile (0,i](0, i](0,i]while i > 0: with i -= i & (-i)
query(r)−query(l−1)\text{query}(r) - \text{query}(l - 1)query(r)−query(l−1)return self.query(right) - self.query(left - 1)
send the change, not the new valueself.fenwick.update(index + 1, val - self.nums[index])

Both walks take O(log⁡n)O(\log n)O(logn) steps. The constructor calls update nnn times, so it takes O(nlog⁡n)O(n \log n)O(nlogn) time. The tree and the copy of nums take O(n)O(n)O(n) space.

Code Shape
Archetype Code Shape
Python
1`tree[i]` holds the sum of the `i & (-i)` positions that end at `i`, so any prefix is a few whole blocks and any position sits in a few blocks.
2`query(i)` adds `tree[i]` and steps `i -= i & (-i)` down to 0; `update(i, delta)` adds `delta` to `tree[i]` and steps `i += i & (-i)` up past `n`.
3`NumArray`: keep `self.nums`, build by calling `update(index + 1, val)` for each value, `update` adds `val - self.nums[index]`, `sumRange` returns `range_query(left + 1, right + 1)`.
4The trap: the tree is 1-indexed. `i & (-i)` is 0 when `i` is 0, so `update(0, ...)` never moves and loops forever; shift every index up by one.

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

A prefix-sum array answers range sums in O(1)O(1)O(1) but needs an O(N)O(N)O(N) rebuild after every change; a plain array updates in O(1)O(1)O(1) but sums in O(N)O(N)O(N). A Fenwick tree (Binary Indexed Tree) meets in the middle: both operations take O(log⁡N)O(\log N)O(logN). Slot tree[i] stores the sum of a block of positions that ends at i and is i & (-i) long (the value of i's lowest set bit).

🏟️ The Analogy: Receipts Filed by Month, Quarter and Year

An accountant keeps receipts in folders of different sizes: some cover one month, some two, some four, some eight, each ending at a certain month. To total January to November, you add a handful of folders that together cover exactly those months. When a March receipt is corrected, you fix every folder that contains March: a handful again.

🔑 Why It Works

Removing the lowest set bit lands just before the block tree[i] covers, so the blocks query adds never overlap and cover exactly 1..i. Adding it reaches the next bigger block that contains i. Both loops touch one block per bit: O(log⁡N)O(\log N)O(logN).

  • Using index 0 in the tree: i & (-i) is 0 when i is 0, so update(0, delta) adds forever without moving. Shift every LeetCode index by one: update(index + 1, ...).

  • Adding val instead of the change: update(index, val) sets a value, but the tree can only add. Add val - self.nums[index] and then store val, or every repeated update double-counts.

  • Off-by-one in sumRange: with 1-indexed slots the sum of nums[left..right] is query(right + 1) - query(left), which is range_query(left + 1, right + 1).

  • Rebuilding a prefix array on every update: correct but O(N) per update; with 3 * 104 calls on 3 * 104 elements that is about 10^9 steps.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"Change one element" mixed with "sum of a contiguous stretch", many times and in any order: a prefix array alone rebuilds in O(N) per change, and a plain array sums in O(N). Point updates plus prefix-based range sums are the exact job of a Fenwick Tree.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

self.tree[i] always equals the sum of the values at positions i - (i & -i) + 1 through i; update keeps it true by walking i += i & (-i), and query(i) adds disjoint blocks walking i -= i & (-i).

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

This problem's traps are in Common Pitfalls above.

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd use a Fenwick Tree, because I need point updates and range sums mixed together. Slot i of the tree stores the sum of a block ending at i whose length is i's lowest set bit. A prefix sum walks down by removing that bit each time, adding whole blocks that never overlap; an update walks up by adding the bit, fixing every block that contains the position. Each walk touches one block per bit, so both are O(log N). In NumArray I keep the current values so an update can add val minus the old value, since the tree only adds, and a range sum is the prefix at right plus one minus the prefix at left. The trap is indexing: the tree is 1-indexed, because at zero the lowest set bit is zero and the loop would never move, so I shift every index up by one. Building costs O(N log N) and space is O(N).

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(log N) update & query

Look at the loops: update runs i += i & (-i) until i > n; each step moves the lowest set bit of i at least one place higher, so it runs at most about log2(N) + 1 times. query runs i -= i & (-i) until i == 0; each step removes one set bit, so it runs at most log2(N) + 1 times. sumRange is two queries and NumArray.update is one tree update: O(log N) each. Building NumArray makes N tree updates: O(N log N) once.

SPACE COMPLEXITY

O(N)

self.tree has N + 1 slots and self.nums keeps N current values: O(N). The loops are iterative, so there is no recursion stack.

Formal Recurrence Relation

T(update) = T(query) = O(log N); T(sumRange) = 2 · O(log N); build = N · O(log N)

Derivation Progression

Build

N × O(log N)

__init__ calls self.fenwick.update(index + 1, val) once per value.

Point update

O(log N)

i += i & (-i) climbs at most one step per bit of N.

Prefix query

O(log N)

i -= i & (-i) clears one set bit per step.

sumRange

2 × O(log N)

range_query is query(right) - query(left - 1).

Variable Definitions

NNN

Length of nums

i&(−i)i \& (-i)i&(−i)

The lowest set bit of i: the length of the block tree[i] covers

Memory Architecture & Bounds

🟣 Call Stack

O(1): both walks are loops

🔵 Auxiliary Heap

O(N): tree (N + 1 slots) and nums

🟢 Output Space

O(1) per call

Boundary Best / Worst Cases

Best Case

O(1)O(1)O(1) steps when i is a large power of two for update, or has one set bit for query

Average Case

O(log⁡N)O(\log N)O(logN) per operation; O(Nlog⁡N)O(N \log N)O(NlogN) to build

Worst Case

O(log⁡N)O(\log N)O(logN) per operation

Recurrence Tree Topology

Recurrence Tree Topology
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "change one element", "sum of a contiguous stretch", "in any order". Point updates mixed with range sums: Fenwick Tree (two prefix sums per range, O(log N) each).

CONSTRAINTS & BOUNDS

N≤3×104N \le 3 \times 10^4N≤3×104 values in [−100,100][-100, 100][−100,100] and at most 3×1043 \times 10^43×104 calls. Sums stay within ±3×106\pm 3 \times 10^6±3×106. Naive: up to 9×1089 \times 10^89×108 steps; Fenwick: about 3×104⋅163 \times 10^4 \cdot 163×104⋅16.

FAANG PRODUCTION TRAPS & EDGE CASES

In a concurrent service, an update is several writes (one per block), so readers must not see a half-applied update: guard the walk with a lock or apply updates from a single writer.

Theory Context•Advanced Data Structures
MediumLC 307

Range Sum Query - Mutable (LeetCode 307)

Target Frequency:GoogleAmazonMeta

You get an integer array nums, followed by a mix of two kinds of requests in any order: change one element, or report the sum of a contiguous stretch of elements.

Build the NumArray class:

  • NumArray(nums) stores the array.
  • update(index, val) sets nums[index] to val.
  • sumRange(left, right) returns nums[left] + nums[left + 1] + ... + nums[right], both ends included (left <= right).

Worked Examples

Example 1
Input:["NumArray", "sumRange", "update", "sumRange"] [[[1, 3, 5]], [0, 2], [1, 2], [0, 2]]
Output:[null, 9, null, 8]
103152update(1, 2)
Explanation: The array starts as [1, 3, 5], so sumRange(0, 2) is 1 + 3 + 5 = 9. update(1, 2) makes it [1, 2, 5], and the next sumRange(0, 2) is 1 + 2 + 5 = 8.

⚖️Formal Constraints & Bounds

  • 1 <= nums.length <= 3 * 104

  • -100 <= nums[i] <= 100

  • 0 <= index < nums.length

  • -100 <= val <= 100

  • 0 <= left <= right < nums.length

  • At most 3 * 104 calls will be made to update and sumRange.

Deep-Dive & Conceptual Insights

Core Insight

Store sums of blocks whose length is the index's lowest set bit: a prefix is the sum of O(log N) disjoint blocks, and a changed position belongs to O(log N) blocks, so both operations walk one block per bit.

Real-World Scenario & Production Applications

Live dashboards and leaderboards that keep running totals while individual values keep changing: bucketed counters (requests per second, sales per day) where a single bucket is corrected and a range total is read on every page load. A plain prefix array would need an O(N) rebuild after every change.

Step-by-Step Execution Trace Table

nums = [1, 3, 5] (n = 3). Each value is added at i = index + 1; i & (-i) decides the jump.

CallWalk of itree[1..3] afterReturns
build: update(1, 1)1 → 2 → 4 (stop, > 3)[1, 1, 0]
build: update(2, 3)2 → 4 (stop)[1, 4, 0]
build: update(3, 5)3 → 4 (stop)[1, 4, 5]
sumRange(0, 2) = query(3) - query(0)query(3): tree[3] + tree[2] = 5 + 4 (3 → 2 → 0)[1, 4, 5]9
update(1, 2): add 2 - 3 = -1 at i = 22 → 4 (stop)[1, 3, 5]null
sumRange(0, 2)query(3): 5 + 3 = 8[1, 3, 5]8
Worked on Paper

The Math, Step by Step

1. The range each tree entry covers

The Fenwick tree numbers the array positions from 1, so position index of nums is i=index+1i = \text{index} + 1i=index+1. Write low(i)\text{low}(i)low(i) for the value of the lowest 1 bit of iii. For example, 666 is 110 in binary, so low(6)=2\text{low}(6) = 2low(6)=2.

Entry tree[i] holds the sum of low(i)\text{low}(i)low(i) values: the positions from i−low(i)+1i - \text{low}(i) + 1i−low(i)+1 to iii. Written as a half-open range, it covers (i−low(i), i](i - \text{low}(i),\ i](i−low(i), i].

iiiiii in binarylow(i)\text{low}(i)low(i) = i & -itree[i] covers
100011(0,1](0, 1](0,1]
200102(0,2](0, 2](0,2]
300111(2,3](2, 3](2,3]
401004(0,4](0, 4](0,4]
501011(4,5](4, 5](4,5]
601102(4,6](4, 6](4,6]
701111(6,7](6, 7](6,7]
810008(0,8](0, 8](0,8]
2. Why i & -i is the lowest 1 bit

In two's complement, −i-i−i is computed as "flip every bit of iii, then add 1". Say iii ends with a 1 followed by kkk zeros. Flipping gives a 0 followed by kkk ones. Adding 1 turns those kkk ones back into zeros and the 0 into a 1. So −i-i−i has the same lowest 1 bit as iii, with the same kkk zeros after it. Every bit to the left of it is flipped.

& keeps a bit only where both numbers have a 1. Only the lowest 1 bit is 1 in both. So i & -i equals low(i)\text{low}(i)low(i). For i=6i = 6i=6: in 8 bits, 666 is 00000110 and −6-6−6 is 11111010, and their & is 00000010, which is 2.

3. Why a query and an update each touch about log⁡n\log nlogn entries

A prefix sum walks down. query(i) adds tree[i], which covers (i−low(i),i](i - \text{low}(i), i](i−low(i),i]. Then it sets i←i−low(i)i \leftarrow i - \text{low}(i)i←i−low(i) and repeats. The next range ends exactly where the last one started. So the ranges fit together without gaps or overlaps, and they cover (0,i](0, i](0,i]. Each step clears the lowest 1 bit of iii, so the loop runs once per 1 bit, at most log⁡2n+1\log_2 n + 1log2​n+1 times. For i=7i = 7i=7 the ranges are (6,7](6, 7](6,7], (4,6](4, 6](4,6] and (0,4](0, 4](0,4].

An update walks up. A change at position iii must reach every entry whose range contains iii. update(i, delta) adds delta to tree[i], then sets i←i+low(i)i \leftarrow i + \text{low}(i)i←i+low(i) and repeats. Adding the lowest 1 bit moves to the next larger entry whose range still reaches down to position iii. Each step makes the lowest 1 bit at least twice as large, so the loop runs at most log⁡2n+1\log_2 n + 1log2​n+1 times. With n=8n = 8n=8, a change at position 3 visits entries 3, 4 and 8. The table of part 1 shows that these are exactly the entries that cover position 3.

A range sum is the difference of two prefix sums, as for a fixed array:

sum(left…right)=query(right)−query(left−1)\text{sum}(\text{left} \ldots \text{right}) = \text{query}(\text{right}) - \text{query}(\text{left} - 1)sum(left…right)=query(right)−query(left−1)

with 1-based positions. sumRange adds 1 to both indices first.

4. The 1-based positions and the size of the change

Position 0 does not work in a Fenwick tree. low(0)=0\text{low}(0) = 0low(0)=0, so update(0, ...) would add 0 to iii forever. That is why the code stores position index at i=index+1i = \text{index} + 1i=index+1, and why tree has n+1n + 1n+1 entries with tree[0] unused. For query(0), the loop while i > 0 runs zero times and returns 0, which is the sum of no elements.

The tree stores sums, so it must receive the change of a value, not the new value. NumArray.update sends val - self.nums[index], and then stores val in self.nums for the next change. Sending val itself would add the new value on top of the old one.

5. Example 1 by hand

nums = [1, 3, 5], so n=3n = 3n=3. The constructor calls update once per value:

CallEntries visitedtree[1..3] after
update(1, 1)1, then 1+1=21 + 1 = 21+1=2[1,1,0][1, 1, 0][1,1,0]
update(2, 3)2 (next would be 4, past nnn)[1,4,0][1, 4, 0][1,4,0]
update(3, 5)3 (next would be 4, past nnn)[1,4,5][1, 4, 5][1,4,5]

So tree[2] = 4 holds positions 1 and 2, and tree[3] = 5 holds position 3 alone.

sumRange(0, 2) computes query(3) - query(0). query(3) adds tree[3] = 5, moves to 3−1=23 - 1 = 23−1=2, adds tree[2] = 4, and moves to 2−2=02 - 2 = 02−2=0. It returns 999. query(0) returns 0. The answer is 999, the expected output.

update(1, 2) changes nums[1] from 3 to 2, so it sends the change 2−3=−12 - 3 = -12−3=−1 to position 2. Entry 2 becomes 4−1=34 - 1 = 34−1=3. The next entry would be 2+2=42 + 2 = 42+2=4, past n=3n = 3n=3, so the update stops.

The second sumRange(0, 2) adds tree[3] = 5 and tree[2] = 3, and returns 888, the expected output.

6. From the math to the code
MathIn the code
positions 1…n1 \ldots n1…n, tree[0] unusedself.tree = [0] * (n + 1) and index + 1
low(i)\text{low}(i)low(i)i & (-i)
walk up to every entry that covers iiiwhile i <= self.n: with i += i & (-i)
walk down over ranges that tile (0,i](0, i](0,i]while i > 0: with i -= i & (-i)
query(r)−query(l−1)\text{query}(r) - \text{query}(l - 1)query(r)−query(l−1)return self.query(right) - self.query(left - 1)
send the change, not the new valueself.fenwick.update(index + 1, val - self.nums[index])

Both walks take O(log⁡n)O(\log n)O(logn) steps. The constructor calls update nnn times, so it takes O(nlog⁡n)O(n \log n)O(nlogn) time. The tree and the copy of nums take O(n)O(n)O(n) space.

Code Shape
Archetype Code Shape
Python
1`tree[i]` holds the sum of the `i & (-i)` positions that end at `i`, so any prefix is a few whole blocks and any position sits in a few blocks.
2`query(i)` adds `tree[i]` and steps `i -= i & (-i)` down to 0; `update(i, delta)` adds `delta` to `tree[i]` and steps `i += i & (-i)` up past `n`.
3`NumArray`: keep `self.nums`, build by calling `update(index + 1, val)` for each value, `update` adds `val - self.nums[index]`, `sumRange` returns `range_query(left + 1, right + 1)`.
4The trap: the tree is 1-indexed. `i & (-i)` is 0 when `i` is 0, so `update(0, ...)` never moves and loops forever; shift every index up by one.

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

A prefix-sum array answers range sums in O(1)O(1)O(1) but needs an O(N)O(N)O(N) rebuild after every change; a plain array updates in O(1)O(1)O(1) but sums in O(N)O(N)O(N). A Fenwick tree (Binary Indexed Tree) meets in the middle: both operations take O(log⁡N)O(\log N)O(logN). Slot tree[i] stores the sum of a block of positions that ends at i and is i & (-i) long (the value of i's lowest set bit).

🏟️ The Analogy: Receipts Filed by Month, Quarter and Year

An accountant keeps receipts in folders of different sizes: some cover one month, some two, some four, some eight, each ending at a certain month. To total January to November, you add a handful of folders that together cover exactly those months. When a March receipt is corrected, you fix every folder that contains March: a handful again.

🔑 Why It Works

Removing the lowest set bit lands just before the block tree[i] covers, so the blocks query adds never overlap and cover exactly 1..i. Adding it reaches the next bigger block that contains i. Both loops touch one block per bit: O(log⁡N)O(\log N)O(logN).

  • Using index 0 in the tree: i & (-i) is 0 when i is 0, so update(0, delta) adds forever without moving. Shift every LeetCode index by one: update(index + 1, ...).

  • Adding val instead of the change: update(index, val) sets a value, but the tree can only add. Add val - self.nums[index] and then store val, or every repeated update double-counts.

  • Off-by-one in sumRange: with 1-indexed slots the sum of nums[left..right] is query(right + 1) - query(left), which is range_query(left + 1, right + 1).

  • Rebuilding a prefix array on every update: correct but O(N) per update; with 3 * 104 calls on 3 * 104 elements that is about 10^9 steps.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"Change one element" mixed with "sum of a contiguous stretch", many times and in any order: a prefix array alone rebuilds in O(N) per change, and a plain array sums in O(N). Point updates plus prefix-based range sums are the exact job of a Fenwick Tree.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

self.tree[i] always equals the sum of the values at positions i - (i & -i) + 1 through i; update keeps it true by walking i += i & (-i), and query(i) adds disjoint blocks walking i -= i & (-i).

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

This problem's traps are in Common Pitfalls above.

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd use a Fenwick Tree, because I need point updates and range sums mixed together. Slot i of the tree stores the sum of a block ending at i whose length is i's lowest set bit. A prefix sum walks down by removing that bit each time, adding whole blocks that never overlap; an update walks up by adding the bit, fixing every block that contains the position. Each walk touches one block per bit, so both are O(log N). In NumArray I keep the current values so an update can add val minus the old value, since the tree only adds, and a range sum is the prefix at right plus one minus the prefix at left. The trap is indexing: the tree is 1-indexed, because at zero the lowest set bit is zero and the loop would never move, so I shift every index up by one. Building costs O(N log N) and space is O(N).

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(log N) update & query

Look at the loops: update runs i += i & (-i) until i > n; each step moves the lowest set bit of i at least one place higher, so it runs at most about log2(N) + 1 times. query runs i -= i & (-i) until i == 0; each step removes one set bit, so it runs at most log2(N) + 1 times. sumRange is two queries and NumArray.update is one tree update: O(log N) each. Building NumArray makes N tree updates: O(N log N) once.

SPACE COMPLEXITY

O(N)

self.tree has N + 1 slots and self.nums keeps N current values: O(N). The loops are iterative, so there is no recursion stack.

Formal Recurrence Relation

T(update) = T(query) = O(log N); T(sumRange) = 2 · O(log N); build = N · O(log N)

Derivation Progression

Build

N × O(log N)

__init__ calls self.fenwick.update(index + 1, val) once per value.

Point update

O(log N)

i += i & (-i) climbs at most one step per bit of N.

Prefix query

O(log N)

i -= i & (-i) clears one set bit per step.

sumRange

2 × O(log N)

range_query is query(right) - query(left - 1).

Variable Definitions

NNN

Length of nums

i&(−i)i \& (-i)i&(−i)

The lowest set bit of i: the length of the block tree[i] covers

Memory Architecture & Bounds

🟣 Call Stack

O(1): both walks are loops

🔵 Auxiliary Heap

O(N): tree (N + 1 slots) and nums

🟢 Output Space

O(1) per call

Boundary Best / Worst Cases

Best Case

O(1)O(1)O(1) steps when i is a large power of two for update, or has one set bit for query

Average Case

O(log⁡N)O(\log N)O(logN) per operation; O(Nlog⁡N)O(N \log N)O(NlogN) to build

Worst Case

O(log⁡N)O(\log N)O(logN) per operation

Recurrence Tree Topology

Recurrence Tree Topology
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "change one element", "sum of a contiguous stretch", "in any order". Point updates mixed with range sums: Fenwick Tree (two prefix sums per range, O(log N) each).

CONSTRAINTS & BOUNDS

N≤3×104N \le 3 \times 10^4N≤3×104 values in [−100,100][-100, 100][−100,100] and at most 3×1043 \times 10^43×104 calls. Sums stay within ±3×106\pm 3 \times 10^6±3×106. Naive: up to 9×1089 \times 10^89×108 steps; Fenwick: about 3×104⋅163 \times 10^4 \cdot 163×104⋅16.

FAANG PRODUCTION TRAPS & EDGE CASES

In a concurrent service, an update is several writes (one per block), so readers must not see a half-applied update: guard the walk with a lock or apply updates from a single writer.

© 2026 Hi👋WebEnterprise. All rights reserved.
Sitemap•llms.txt•