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).

12 Core Algorithmic Patterns & 168 Practice Problems

  • 1. Two Pointers (9 Paradigms, 32 Problems): Converging pointers, sorted pair sums, container with most water, trapping rain water, 3Sum, plus the Sliding Window and Fast & Slow Pointers paradigms (Floyd cycle detection, monotonic window invariants, longest substrings, minimum window).
  • 2. Binary Search (8 Paradigms, 12 Problems): Monotonic predicate partitioning, boundary searching, rotated arrays, median of two sorted arrays, matrix median on value range.
  • 3. Bit Manipulation (5 Paradigms, 8 Problems): Bitmasking, XOR tricks, counting set bits, subset enumeration via bitmasks.
  • 4. Math & Geometry (5 Paradigms, 10 Problems): Sieve of Eratosthenes, integer/roman conversions, modular arithmetic, geometric simulation.
  • 5. Tree/Graph Depth-First Search (8 Paradigms, 17 Problems): Path sums, lowest common ancestor, tree diameter, subtree serialization, validating BSTs.
  • 6. Tree/Graph Breadth-First Search (4 Paradigms, 11 Problems): Level-order traversals, shortest path, rotting oranges, word ladders.
  • 7. Graphs (6 Paradigms, 14 Problems): Topological sort, cycle detection, Dijkstra, bipartite validation, network delay.
  • 8. Backtracking (5 Paradigms, 13 Problems): Subsets, permutations, combinations, constraint satisfaction, pruning, N-Queens.
  • 9. Dynamic Programming (7 Paradigms, 14 Problems): Memoization vs tabulation, knapsack, coin change, edit distance, longest common subsequence.
  • 10. Heap / Priority Queue (8 Paradigms, 10 Problems): Running medians, top-k elements, interval scheduling, IPO, k-way merges.
  • 11. Advanced Data Structures (5 Paradigms, 13 Problems): Trie, Union-Find, LRU Cache, LFU Cache, Monotonic Stacks.
  • 12. Intervals & Stack / Miscellaneous (7 Paradigms, 14 Problems): Merge intervals, daily temperatures, largest rectangle in histogram, trapping rain water via stack.

4-Stage Deliberate Practice Framework

  1. Stage 1 (Compare & Learn): Multi-language Rosetta Stone contrasting abstract invariants with concrete solutions across Python, C#, Java, TypeScript, C++, Go, and Rust.
  2. Stage 2 (Active Recall): Reconstruct algorithmic template invariants from memory with real-time feedback before looking at solutions.
  3. Stage 3 (Senior SWE AI Mock Coach): Simulated senior mock interview evaluating Big-O space/time tradeoffs, edge cases, and code reviews in Monaco Editor.
  4. Stage 4 (Solve on Your Own): Timed sandbox challenges verified against automated test suites in Python, C#, Java, and TypeScript.

Equipped with SM-2 Spaced Repetition Review Hub, Studio Cockpit workspace layout, and interactive study notes.

Pricing, Access & Commercial Terms

  • Core Curriculum: 100% Free. No credit card required.
  • Compute Coins: 40 free coins upon signup, +20 daily login bonus, +25 referral bonus.
  • 24-Hour AI Coaching Pass: 5 compute coins unlocks unlimited senior SWE AI coaching for a full 24 hours.
  • BYOK (Bring Your Own Key): Completely free unlimited AI coaching if using your own Gemini/OpenAI API key.
  • Refund & Subscription Policy: No recurring charges, no subscription traps, and no paid paywalls. Free tier provides full learning path.
  • Platform Operator: Hi👋WebEnterprise Inc. Support & policies at hispeedalgo.com.
Skip to main content
Hi👋SpeedAlgo

Invariant-First Algorithmic Mastery

180Items
Theory Context•Advanced Data Structures
MediumLC 307

Range Sum Query - Mutable (LeetCode 307)

You will see how a Fenwick tree answers range sums and point updates in O(log N) each by storing blocks sized by the lowest set bit.

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

Why It Works & Core Invariant

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
Scroll horizontally to see all columns, or expand to full screen
Core Invariant Specification & 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.

Target: Range Sum Query - Mutable (LeetCode 307). Slot 0 is unused: `i & (-i)` is 0 at i = 0, so the walks could never move from there.

Boundary Model: Trie Prefix Tree Branching / DSU Near-Flat Forest

Trie: root-to-node path encodes common prefix; DSU: find(u) with path compression flattens tree so root is direct parent.

Loop Invariant Termination

Trie: traverse word char-by-char in O(L); DSU: if find(u) != find(v): union(u, v) in O(alpha(N)).

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.

🪄 The Mathematical Harmony / Magic Trick
Code / Blueprint
def query(self, i): # sum of positions 1..i
total = 0
while i > 0:
total += self.tree[i]
i -= i & (-i) # jump to just before this block
return total
 
def update(self, i, delta): # add delta at position i
while i <= self.n:
self.tree[i] += delta
i += i & (-i) # jump to the next block that also contains i
 

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).

💡 Summary

The tree is 1-indexed: i & (-i) is 0 when i is 0, so position 0 would never move. NumArray shifts every index up by one, keeps the current values to turn "set to val" into "add val - old", and answers sumRange(left, right) as query(right + 1) - query(left).

  • 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

You will see how a senior engineer picks a Fenwick tree for point updates plus range sums, and names its indexing trap.

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

  • self.fenwick.update(index + 1, val): the tree is 1-indexed; i & (-i) is 0 at i = 0, so position 0 would loop forever.

  • val - self.nums[index]: the tree adds, so an update must add the change and then store the new value.

  • range_query(left + 1, right + 1) = query(right + 1) - query(left): both ends shifted by one.

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).

So: two prefix sums per range, one walk per bit, 1-indexed slots, and add val - old on update.

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)

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.

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)

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

Index 0 loops forever. 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.

Core Algorithmic State Invariants

1. Block Invariant

`self.tree[i]` always equals the sum of the values at positions `i - (i & -i) + 1` through `i`: a block ending at `i` whose length is `i`'s lowest set bit.

2. Walk Down to Sum, Up to Update

`query` adds `tree[i]` and removes the lowest set bit, so its blocks never overlap and cover exactly `1..i`; `update` adds the lowest set bit to reach every larger block containing `i`.

3. 1-Indexed, O(log N)

Slot 0 is never used (`0 & -0 == 0` would loop forever), so LeetCode index `k` lives at slot `k + 1`. Each walk takes at most one step per bit: O(log N) per call, O(N) space.

Theory Context•Advanced Data Structures
MediumLC 307

Range Sum Query - Mutable (LeetCode 307)

You will see how a Fenwick tree answers range sums and point updates in O(log N) each by storing blocks sized by the lowest set bit.

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

Why It Works & Core Invariant

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
Scroll horizontally to see all columns, or expand to full screen
Core Invariant Specification & 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.

Target: Range Sum Query - Mutable (LeetCode 307). Slot 0 is unused: `i & (-i)` is 0 at i = 0, so the walks could never move from there.

Boundary Model: Trie Prefix Tree Branching / DSU Near-Flat Forest

Trie: root-to-node path encodes common prefix; DSU: find(u) with path compression flattens tree so root is direct parent.

Loop Invariant Termination

Trie: traverse word char-by-char in O(L); DSU: if find(u) != find(v): union(u, v) in O(alpha(N)).

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.

🪄 The Mathematical Harmony / Magic Trick
Code / Blueprint
def query(self, i): # sum of positions 1..i
total = 0
while i > 0:
total += self.tree[i]
i -= i & (-i) # jump to just before this block
return total
 
def update(self, i, delta): # add delta at position i
while i <= self.n:
self.tree[i] += delta
i += i & (-i) # jump to the next block that also contains i
 

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).

💡 Summary

The tree is 1-indexed: i & (-i) is 0 when i is 0, so position 0 would never move. NumArray shifts every index up by one, keeps the current values to turn "set to val" into "add val - old", and answers sumRange(left, right) as query(right + 1) - query(left).

  • 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

You will see how a senior engineer picks a Fenwick tree for point updates plus range sums, and names its indexing trap.

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

  • self.fenwick.update(index + 1, val): the tree is 1-indexed; i & (-i) is 0 at i = 0, so position 0 would loop forever.

  • val - self.nums[index]: the tree adds, so an update must add the change and then store the new value.

  • range_query(left + 1, right + 1) = query(right + 1) - query(left): both ends shifted by one.

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).

So: two prefix sums per range, one walk per bit, 1-indexed slots, and add val - old on update.

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)

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.

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)

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

Index 0 loops forever. 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.

Core Algorithmic State Invariants

1. Block Invariant

`self.tree[i]` always equals the sum of the values at positions `i - (i & -i) + 1` through `i`: a block ending at `i` whose length is `i`'s lowest set bit.

2. Walk Down to Sum, Up to Update

`query` adds `tree[i]` and removes the lowest set bit, so its blocks never overlap and cover exactly `1..i`; `update` adds the lowest set bit to reach every larger block containing `i`.

3. 1-Indexed, O(log N)

Slot 0 is never used (`0 & -0 == 0` would loop forever), so LeetCode index `k` lives at slot `k + 1`. Each walk takes at most one step per bit: O(log N) per call, O(N) space.

Rosetta Dual-Monaco Comparison
Python 3
CANONICAL INVARIANT TEMPLATE
Loading...
CONCRETE: RANGE SUM QUERY - MUTABLE (LEETCODE 307)
T = O(log N) update & queryS = O(N)
Loading...
Pattern Implementation Mapping TableCanonical Invariant ⟷ Concrete Code ⟷ Engineering Rationale
Canonical InvariantConcrete CodeEngineering Rationale
One slot per position, 1-indexedself.tree = [0] * (n + 1)Slot 0 is unused: `i & (-i)` is 0 at i = 0, so the walks could never move from there.
Point update: fix every block that contains iwhile i <= self.n: self.tree[i] += delta i += i & (-i)Adding the lowest set bit jumps to the next larger block that also covers position i.
Prefix query: add disjoint blocks that cover 1..iwhile i > 0: total += self.tree[i] i -= i & (-i)Removing the lowest set bit jumps to just before the current block, so no position is counted twice.
Shift 0-indexed positions to 1-indexed slotsself.fenwick.update(index + 1, val)LeetCode's index 0 lives at slot 1.
Turn 'set to val' into 'add a change'self.fenwick.update(index + 1, val - self.nums[index]) self.nums[index] = valThe tree only adds; keeping the current values gives the difference to add.
Range sum from two prefix sumsreturn self.fenwick.range_query(left + 1, right + 1)`range_query(l, r) = query(r) - query(l - 1)`: everything before `left` cancels.
© 2026 Hi👋WebEnterprise. All rights reserved.
Sitemap•llms.txt•