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•Two Pointers & Sliding Window
MediumLC 238

Product of Array Except Self (LeetCode 238)

Target Frequency:AmazonMetaGoogleApple

You have an integer array nums. Build an output array answer where each answer[i] holds the product of every element of nums other than nums[i] itself.

Every prefix or suffix product you'll ever need to compute is guaranteed to fit inside a 32-bit integer.

Solve it in O(n) time, and without dividing.

Worked Examples

Example 1
Input:nums = [1,2,3,4]
Output:[24,12,8,6]
Explanation: For index 0: 234 = 24; index 1: 134 = 12; index 2: 124 = 8; index 3: 123 = 6.
Example 2
Input:nums = [-1,1,0,-3,3]
Output:[0,0,9,0,0]
Explanation: Index 2 has product (-1)1(-3)*3 = 9. All other indices include the factor 0, resulting in 0.

⚖️Formal Constraints & Bounds

  • 2 <= nums.length <= 105

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

  • The input is generated such that answer[i] is guaranteed to fit in a 32-bit integer.

Deep-Dive & Conceptual Insights

Core Insight

Everything except nums[i] is everything left of i times everything right of i. A left pass stores the left product in res[i] and a right pass multiplies in the right product, so no division is ever needed.

Real-World Scenario & Production Applications

Bayesian probability updates (independent evidence combination), graphics rendering light transport equations, and zero-safe arithmetic pipeline processing.

Subproblems & Recurrence Decomposition3 Phases

🧩Subproblem 1: Left Pass

Walk left to right and store in res[i] the product of everything strictly left of i. Write res[i] first, then fold nums[i] into prefix.

Mathematical Recurrence / Code Invariant
res = [1] * n
prefix = 1
for i in range(n):
    res[i] = prefix
    prefix *= nums[i]

Step-by-Step Execution Trace Table

Indexnums[i]Prefix Product PassSuffix MultiplierFinal res[i]
0112424
1211212
23248
34616
Worked on Paper

The Math, Step by Step

1. The number the code builds for each index

For each index iii, the answer is the product of every element except nums[i]. Split those elements into two groups: the elements to the left of iii and the elements to the right of iii.

L[i]=nums[0]⋅nums[1]⋯nums[i−1]R[i]=nums[i+1]⋯nums[n−1]L[i] = \text{nums}[0] \cdot \text{nums}[1] \cdots \text{nums}[i - 1] \qquad R[i] = \text{nums}[i + 1] \cdots \text{nums}[n - 1]L[i]=nums[0]⋅nums[1]⋯nums[i−1]R[i]=nums[i+1]⋯nums[n−1]

L[i]L[i]L[i] is the prefix product before iii, and R[i]R[i]R[i] is the suffix product after iii. Every element except nums[i] is in exactly one of the two groups, so

answer[i]=L[i]⋅R[i]\text{answer}[i] = L[i] \cdot R[i]answer[i]=L[i]⋅R[i]

2. Each product from the one before it

A product with no factors is 111, called the empty product. So L[0]=1L[0] = 1L[0]=1, because nothing is to the left of index 0. R[n−1]=1R[n - 1] = 1R[n−1]=1, because nothing is to the right of the last index. Every other value takes one multiplication:

L[i+1]=L[i]⋅nums[i]R[i−1]=R[i]⋅nums[i]L[i + 1] = L[i] \cdot \text{nums}[i] \qquad R[i - 1] = R[i] \cdot \text{nums}[i]L[i+1]=L[i]⋅nums[i]R[i−1]=R[i]⋅nums[i]

So one pass from left to right builds every L[i]L[i]L[i], and one pass from right to left builds every R[i]R[i]R[i].

3. Why no division is needed, and why zeros are safe

A shorter idea is "the product of all elements, divided by nums[i]". It fails when nums[i] is 0, because you cannot divide by 0. The problem also forbids division.

L[i]⋅R[i]L[i] \cdot R[i]L[i]⋅R[i] only multiplies. A zero in nums is multiplied into every LLL to its right and every RRR to its left. It is never a factor of its own index's answer. So Example 2, which has a zero, needs no special case.

4. Write first, then multiply

In the left pass, the code writes res[i] = prefix before it runs prefix *= nums[i]. At that moment prefix is still L[i]L[i]L[i], the product of the elements before iii. If the two lines were swapped, res[i] would get L[i]⋅nums[i]L[i] \cdot \text{nums}[i]L[i]⋅nums[i] and include nums[i] itself. The right pass uses the same order: res[i] *= suffix runs before suffix *= nums[i].

The right pass also uses *=, not =. res[i] already holds L[i]L[i]L[i] from the left pass. Multiplying keeps it, so res[i] becomes L[i]⋅R[i]L[i] \cdot R[i]L[i]⋅R[i].

5. Example 1 by hand

nums = [1,2,3,4].

iiinums[i]L[i]L[i]L[i]R[i]R[i]R[i]L[i]⋅R[i]L[i] \cdot R[i]L[i]⋅R[i]
01111 (empty)2⋅3⋅4=242 \cdot 3 \cdot 4 = 242⋅3⋅4=24242424
121113⋅4=123 \cdot 4 = 123⋅4=12121212
231⋅2=21 \cdot 2 = 21⋅2=2444888
341⋅2⋅3=61 \cdot 2 \cdot 3 = 61⋅2⋅3=6111 (empty)666

After the left pass, res = [1, 1, 2, 6], the LLL column. The right pass multiplies each entry by the RRR column, [24, 12, 4, 1]. The result [24, 12, 8, 6] is the expected output.

6. Example 2 by hand

nums = [-1,1,0,-3,3].

iiinums[i]L[i]L[i]L[i]R[i]R[i]R[i]L[i]⋅R[i]L[i] \cdot R[i]L[i]⋅R[i]
0−1-1−1111 (empty)1⋅0⋅(−3)⋅3=01 \cdot 0 \cdot (-3) \cdot 3 = 01⋅0⋅(−3)⋅3=0000
1111−1-1−10⋅(−3)⋅3=00 \cdot (-3) \cdot 3 = 00⋅(−3)⋅3=0000
2000(−1)⋅1=−1(-1) \cdot 1 = -1(−1)⋅1=−1(−3)⋅3=−9(-3) \cdot 3 = -9(−3)⋅3=−9999
3−3-3−3(−1)⋅1⋅0=0(-1) \cdot 1 \cdot 0 = 0(−1)⋅1⋅0=0333000
4333000111 (empty)000

Only index 2 has the zero outside both groups. Its answer is (−1)⋅(−9)=9(-1) \cdot (-9) = 9(−1)⋅(−9)=9. Every other index has the zero in LLL or in RRR, so its answer is 000. The result [0, 0, 9, 0, 0] is the expected output.

7. From the formulas to the code
FormulaIn productExceptSelf
L[0]=1L[0] = 1L[0]=1prefix = 1
res[i] gets L[i]L[i]L[i]res[i] = prefix
L[i+1]=L[i]⋅nums[i]L[i + 1] = L[i] \cdot \text{nums}[i]L[i+1]=L[i]⋅nums[i]prefix *= nums[i]
R[n−1]=1R[n - 1] = 1R[n−1]=1suffix = 1
res[i] becomes L[i]⋅R[i]L[i] \cdot R[i]L[i]⋅R[i]res[i] *= suffix
R[i−1]=R[i]⋅nums[i]R[i - 1] = R[i] \cdot \text{nums}[i]R[i−1]=R[i]⋅nums[i]suffix *= nums[i]

Each pass takes O(n)O(n)O(n) time. Besides the output array res, the code keeps only prefix and suffix, so it uses O(1)O(1)O(1) extra space.

Code Shape
Archetype Code Shape
Python
1res = [1] * n
2prefix = 1
3for i in range(n):
4 res[i] = prefix
5 prefix *= nums[i]
6suffix = 1
7for i in range(n - 1, -1, -1):
8 res[i] *= suffix
9 suffix *= nums[i]
10return res

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

The obvious shortcut — multiply everything, then divide by nums[i] — is banned, and for good reason: any zero in the array breaks division entirely. The prefix/suffix trick sidesteps division altogether.

👥 The Analogy: Everyone Except You, From Two Directions

"The product of everyone except me" is really just "everyone standing to my left, multiplied by everyone standing to my right." So make two separate passes: walking left to right, hand each person the running product of everyone before them. Then walk right to left, multiplying each person's number by the running product of everyone after them. By the time both passes are done, every person is holding exactly "the product of everyone except themselves" — built entirely from multiplication, no division anywhere.

🔑 The Key Idea in the Code

Each index only ever needs its own left-total and right-total — never the array's overall total — which is exactly what makes skipping division possible.

  • Folding too early: prefix *= nums[i] before res[i] = prefix (or suffix *= nums[i] before res[i] *= suffix) multiplies nums[i] into its own answer. On [1,2,3,4] the left pass alone already stores [1,2,6,24] instead of [1,1,2,6].

  • Overwriting in the second pass: res[i] = suffix instead of res[i] *= suffix throws away the left product the first pass stored.

  • Wrong start value: prefix and suffix start at 1, the empty product. Starting at 0 makes every answer 0.

  • Dividing a total product: total // nums[i] is not allowed here, and it breaks as soon as nums contains a zero. Without division, one zero leaves a single non-zero answer and two zeros make every answer 0, with no special case.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

The statement asks, for every index, for "the product of every element of nums other than nums[i]": each answer needs everything on its left and everything on its right. "Without dividing" rules out total // nums[i], and "O(n) time" rules out multiplying the other N - 1 numbers again for every index.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Invariant: when res[i] = prefix runs, prefix is the product of nums[0..i-1]; when res[i] *= suffix runs, suffix is the product of nums[i+1..n-1]. Neither ever contains nums[i], so after both passes res[i] is the product of all the other numbers.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • Keeping two extra arrays: a full left array and a full right array also work, but they cost O(N) extra space. The follow-up asks for O(1) extra besides the output, so store the left products in res itself and carry the right side as the single number suffix.

The 60-Second Interview Pitch

Say this out loud before you type a single line

"I'll use a Prefix-Suffix Scan. Each answer is the product of everything left of i times everything right of i, and each side is a running product I can carry in one sweep. In the first pass, left to right, I write res[i] = prefix and only then fold nums[i] into prefix, so prefix only ever holds the numbers before i. In the second pass, right to left, I multiply res[i] by suffix and then fold nums[i] into suffix. I never divide, so zeros need no special case: one zero leaves a single non-zero answer, and two zeros make every answer zero. Two linear passes give O(N) time, and besides the output array I keep only prefix and suffix, so O(1) extra space."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N)

Look at the code: res = [1] * n writes N slots. for i in range(n) runs N times and does one write (res[i] = prefix) and one multiplication (prefix *= nums[i]). for i in range(n - 1, -1, -1) runs N times and does two multiplications (res[i] *= suffix, suffix *= nums[i]). No loop is nested inside another, so the total is N + 2N + 2N = 5N constant-time steps.

SPACE COMPLEXITY

O(1) extra, output not counted

Look at the allocations: res is the answer the problem asks for (N integers, output, not counted). Besides it the code keeps n, prefix, suffix and i: four integers, whatever N is. There is no recursion, so the call stack stays constant.

Formal Recurrence Relation

T(N) = N (fill res) + 2N (left pass) + 2N (right pass) = 5N = O(N)

Derivation Progression

Fill res

res = [1] * n -> N writes

Every slot starts at 1, the empty product.

Left pass

for i in range(n): 1 write + 1 multiply -> 2N

res[i] = prefix stores the product of nums[0..i-1]; prefix *= nums[i] then extends it by one element. Each iteration is constant work.

Right pass

for i in range(n - 1, -1, -1): 2 multiplies -> 2N

res[i] *= suffix multiplies in the product of nums[i+1..n-1]; suffix *= nums[i] then extends it by one element.

Total

N + 2N + 2N = 5N = O(N)

The passes run one after the other, never nested, so their costs add up instead of multiplying.

Variable Definitions

NNN

len(nums), the number of elements (the code's n)

Memory Architecture & Bounds

🟣 Call Stack

O(1): no recursion

🔵 Auxiliary Heap

O(1): the integers n, prefix, suffix and i

🟢 Output Space

O(N): res, the answer itself, not counted as extra space

Boundary Best / Worst Cases

Best Case

O(N)O(N)O(N): both passes always visit every index

Average Case

O(N)O(N)O(N)

Worst Case

O(N)O(N)O(N): zeros or negative numbers change no step

Pointer Invariant Transition Progression

Pointer Invariant Transition Progression
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "the product of every element other than nums[i]", "without dividing", "O(n) time". Each answer needs everything on its left and everything on its right: a Prefix-Suffix Scan, one pass from each end.

CONSTRAINTS & BOUNDS

2≤N≤1052 \le N \le 10^52≤N≤105 and −30≤nums[i]≤30-30 \le nums[i] \le 30−30≤nums[i]≤30. Multiplying the other N−1N - 1N−1 numbers again for every index is up to 101010^{10}1010 multiplications; two passes are 2×1052 \times 10^52×105. Every prefix and suffix product is guaranteed to fit in 32 bits. The two passes read the array front to back, then back to front, so they stream well and stay cache-friendly; the only stored array is res, the output.

FAANG PRODUCTION TRAPS & EDGE CASES

At scale the risk is the size of the numbers, not the loop. This problem guarantees every prefix and suffix product fits in 32 bits; outside that guarantee, a running product of many values overflows a fixed-width integer, so production code checks the range or uses a wider type.

Theory Context•Two Pointers & Sliding Window
MediumLC 238

Product of Array Except Self (LeetCode 238)

Target Frequency:AmazonMetaGoogleApple

You have an integer array nums. Build an output array answer where each answer[i] holds the product of every element of nums other than nums[i] itself.

Every prefix or suffix product you'll ever need to compute is guaranteed to fit inside a 32-bit integer.

Solve it in O(n) time, and without dividing.

Worked Examples

Example 1
Input:nums = [1,2,3,4]
Output:[24,12,8,6]
Explanation: For index 0: 234 = 24; index 1: 134 = 12; index 2: 124 = 8; index 3: 123 = 6.
Example 2
Input:nums = [-1,1,0,-3,3]
Output:[0,0,9,0,0]
Explanation: Index 2 has product (-1)1(-3)*3 = 9. All other indices include the factor 0, resulting in 0.

⚖️Formal Constraints & Bounds

  • 2 <= nums.length <= 105

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

  • The input is generated such that answer[i] is guaranteed to fit in a 32-bit integer.

Deep-Dive & Conceptual Insights

Core Insight

Everything except nums[i] is everything left of i times everything right of i. A left pass stores the left product in res[i] and a right pass multiplies in the right product, so no division is ever needed.

Real-World Scenario & Production Applications

Bayesian probability updates (independent evidence combination), graphics rendering light transport equations, and zero-safe arithmetic pipeline processing.

Subproblems & Recurrence Decomposition3 Phases

🧩Subproblem 1: Left Pass

Walk left to right and store in res[i] the product of everything strictly left of i. Write res[i] first, then fold nums[i] into prefix.

Mathematical Recurrence / Code Invariant
res = [1] * n
prefix = 1
for i in range(n):
    res[i] = prefix
    prefix *= nums[i]

Step-by-Step Execution Trace Table

Indexnums[i]Prefix Product PassSuffix MultiplierFinal res[i]
0112424
1211212
23248
34616
Worked on Paper

The Math, Step by Step

1. The number the code builds for each index

For each index iii, the answer is the product of every element except nums[i]. Split those elements into two groups: the elements to the left of iii and the elements to the right of iii.

L[i]=nums[0]⋅nums[1]⋯nums[i−1]R[i]=nums[i+1]⋯nums[n−1]L[i] = \text{nums}[0] \cdot \text{nums}[1] \cdots \text{nums}[i - 1] \qquad R[i] = \text{nums}[i + 1] \cdots \text{nums}[n - 1]L[i]=nums[0]⋅nums[1]⋯nums[i−1]R[i]=nums[i+1]⋯nums[n−1]

L[i]L[i]L[i] is the prefix product before iii, and R[i]R[i]R[i] is the suffix product after iii. Every element except nums[i] is in exactly one of the two groups, so

answer[i]=L[i]⋅R[i]\text{answer}[i] = L[i] \cdot R[i]answer[i]=L[i]⋅R[i]

2. Each product from the one before it

A product with no factors is 111, called the empty product. So L[0]=1L[0] = 1L[0]=1, because nothing is to the left of index 0. R[n−1]=1R[n - 1] = 1R[n−1]=1, because nothing is to the right of the last index. Every other value takes one multiplication:

L[i+1]=L[i]⋅nums[i]R[i−1]=R[i]⋅nums[i]L[i + 1] = L[i] \cdot \text{nums}[i] \qquad R[i - 1] = R[i] \cdot \text{nums}[i]L[i+1]=L[i]⋅nums[i]R[i−1]=R[i]⋅nums[i]

So one pass from left to right builds every L[i]L[i]L[i], and one pass from right to left builds every R[i]R[i]R[i].

3. Why no division is needed, and why zeros are safe

A shorter idea is "the product of all elements, divided by nums[i]". It fails when nums[i] is 0, because you cannot divide by 0. The problem also forbids division.

L[i]⋅R[i]L[i] \cdot R[i]L[i]⋅R[i] only multiplies. A zero in nums is multiplied into every LLL to its right and every RRR to its left. It is never a factor of its own index's answer. So Example 2, which has a zero, needs no special case.

4. Write first, then multiply

In the left pass, the code writes res[i] = prefix before it runs prefix *= nums[i]. At that moment prefix is still L[i]L[i]L[i], the product of the elements before iii. If the two lines were swapped, res[i] would get L[i]⋅nums[i]L[i] \cdot \text{nums}[i]L[i]⋅nums[i] and include nums[i] itself. The right pass uses the same order: res[i] *= suffix runs before suffix *= nums[i].

The right pass also uses *=, not =. res[i] already holds L[i]L[i]L[i] from the left pass. Multiplying keeps it, so res[i] becomes L[i]⋅R[i]L[i] \cdot R[i]L[i]⋅R[i].

5. Example 1 by hand

nums = [1,2,3,4].

iiinums[i]L[i]L[i]L[i]R[i]R[i]R[i]L[i]⋅R[i]L[i] \cdot R[i]L[i]⋅R[i]
01111 (empty)2⋅3⋅4=242 \cdot 3 \cdot 4 = 242⋅3⋅4=24242424
121113⋅4=123 \cdot 4 = 123⋅4=12121212
231⋅2=21 \cdot 2 = 21⋅2=2444888
341⋅2⋅3=61 \cdot 2 \cdot 3 = 61⋅2⋅3=6111 (empty)666

After the left pass, res = [1, 1, 2, 6], the LLL column. The right pass multiplies each entry by the RRR column, [24, 12, 4, 1]. The result [24, 12, 8, 6] is the expected output.

6. Example 2 by hand

nums = [-1,1,0,-3,3].

iiinums[i]L[i]L[i]L[i]R[i]R[i]R[i]L[i]⋅R[i]L[i] \cdot R[i]L[i]⋅R[i]
0−1-1−1111 (empty)1⋅0⋅(−3)⋅3=01 \cdot 0 \cdot (-3) \cdot 3 = 01⋅0⋅(−3)⋅3=0000
1111−1-1−10⋅(−3)⋅3=00 \cdot (-3) \cdot 3 = 00⋅(−3)⋅3=0000
2000(−1)⋅1=−1(-1) \cdot 1 = -1(−1)⋅1=−1(−3)⋅3=−9(-3) \cdot 3 = -9(−3)⋅3=−9999
3−3-3−3(−1)⋅1⋅0=0(-1) \cdot 1 \cdot 0 = 0(−1)⋅1⋅0=0333000
4333000111 (empty)000

Only index 2 has the zero outside both groups. Its answer is (−1)⋅(−9)=9(-1) \cdot (-9) = 9(−1)⋅(−9)=9. Every other index has the zero in LLL or in RRR, so its answer is 000. The result [0, 0, 9, 0, 0] is the expected output.

7. From the formulas to the code
FormulaIn productExceptSelf
L[0]=1L[0] = 1L[0]=1prefix = 1
res[i] gets L[i]L[i]L[i]res[i] = prefix
L[i+1]=L[i]⋅nums[i]L[i + 1] = L[i] \cdot \text{nums}[i]L[i+1]=L[i]⋅nums[i]prefix *= nums[i]
R[n−1]=1R[n - 1] = 1R[n−1]=1suffix = 1
res[i] becomes L[i]⋅R[i]L[i] \cdot R[i]L[i]⋅R[i]res[i] *= suffix
R[i−1]=R[i]⋅nums[i]R[i - 1] = R[i] \cdot \text{nums}[i]R[i−1]=R[i]⋅nums[i]suffix *= nums[i]

Each pass takes O(n)O(n)O(n) time. Besides the output array res, the code keeps only prefix and suffix, so it uses O(1)O(1)O(1) extra space.

Code Shape
Archetype Code Shape
Python
1res = [1] * n
2prefix = 1
3for i in range(n):
4 res[i] = prefix
5 prefix *= nums[i]
6suffix = 1
7for i in range(n - 1, -1, -1):
8 res[i] *= suffix
9 suffix *= nums[i]
10return res

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

The obvious shortcut — multiply everything, then divide by nums[i] — is banned, and for good reason: any zero in the array breaks division entirely. The prefix/suffix trick sidesteps division altogether.

👥 The Analogy: Everyone Except You, From Two Directions

"The product of everyone except me" is really just "everyone standing to my left, multiplied by everyone standing to my right." So make two separate passes: walking left to right, hand each person the running product of everyone before them. Then walk right to left, multiplying each person's number by the running product of everyone after them. By the time both passes are done, every person is holding exactly "the product of everyone except themselves" — built entirely from multiplication, no division anywhere.

🔑 The Key Idea in the Code

Each index only ever needs its own left-total and right-total — never the array's overall total — which is exactly what makes skipping division possible.

  • Folding too early: prefix *= nums[i] before res[i] = prefix (or suffix *= nums[i] before res[i] *= suffix) multiplies nums[i] into its own answer. On [1,2,3,4] the left pass alone already stores [1,2,6,24] instead of [1,1,2,6].

  • Overwriting in the second pass: res[i] = suffix instead of res[i] *= suffix throws away the left product the first pass stored.

  • Wrong start value: prefix and suffix start at 1, the empty product. Starting at 0 makes every answer 0.

  • Dividing a total product: total // nums[i] is not allowed here, and it breaks as soon as nums contains a zero. Without division, one zero leaves a single non-zero answer and two zeros make every answer 0, with no special case.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

The statement asks, for every index, for "the product of every element of nums other than nums[i]": each answer needs everything on its left and everything on its right. "Without dividing" rules out total // nums[i], and "O(n) time" rules out multiplying the other N - 1 numbers again for every index.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Invariant: when res[i] = prefix runs, prefix is the product of nums[0..i-1]; when res[i] *= suffix runs, suffix is the product of nums[i+1..n-1]. Neither ever contains nums[i], so after both passes res[i] is the product of all the other numbers.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • Keeping two extra arrays: a full left array and a full right array also work, but they cost O(N) extra space. The follow-up asks for O(1) extra besides the output, so store the left products in res itself and carry the right side as the single number suffix.

The 60-Second Interview Pitch

Say this out loud before you type a single line

"I'll use a Prefix-Suffix Scan. Each answer is the product of everything left of i times everything right of i, and each side is a running product I can carry in one sweep. In the first pass, left to right, I write res[i] = prefix and only then fold nums[i] into prefix, so prefix only ever holds the numbers before i. In the second pass, right to left, I multiply res[i] by suffix and then fold nums[i] into suffix. I never divide, so zeros need no special case: one zero leaves a single non-zero answer, and two zeros make every answer zero. Two linear passes give O(N) time, and besides the output array I keep only prefix and suffix, so O(1) extra space."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N)

Look at the code: res = [1] * n writes N slots. for i in range(n) runs N times and does one write (res[i] = prefix) and one multiplication (prefix *= nums[i]). for i in range(n - 1, -1, -1) runs N times and does two multiplications (res[i] *= suffix, suffix *= nums[i]). No loop is nested inside another, so the total is N + 2N + 2N = 5N constant-time steps.

SPACE COMPLEXITY

O(1) extra, output not counted

Look at the allocations: res is the answer the problem asks for (N integers, output, not counted). Besides it the code keeps n, prefix, suffix and i: four integers, whatever N is. There is no recursion, so the call stack stays constant.

Formal Recurrence Relation

T(N) = N (fill res) + 2N (left pass) + 2N (right pass) = 5N = O(N)

Derivation Progression

Fill res

res = [1] * n -> N writes

Every slot starts at 1, the empty product.

Left pass

for i in range(n): 1 write + 1 multiply -> 2N

res[i] = prefix stores the product of nums[0..i-1]; prefix *= nums[i] then extends it by one element. Each iteration is constant work.

Right pass

for i in range(n - 1, -1, -1): 2 multiplies -> 2N

res[i] *= suffix multiplies in the product of nums[i+1..n-1]; suffix *= nums[i] then extends it by one element.

Total

N + 2N + 2N = 5N = O(N)

The passes run one after the other, never nested, so their costs add up instead of multiplying.

Variable Definitions

NNN

len(nums), the number of elements (the code's n)

Memory Architecture & Bounds

🟣 Call Stack

O(1): no recursion

🔵 Auxiliary Heap

O(1): the integers n, prefix, suffix and i

🟢 Output Space

O(N): res, the answer itself, not counted as extra space

Boundary Best / Worst Cases

Best Case

O(N)O(N)O(N): both passes always visit every index

Average Case

O(N)O(N)O(N)

Worst Case

O(N)O(N)O(N): zeros or negative numbers change no step

Pointer Invariant Transition Progression

Pointer Invariant Transition Progression
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "the product of every element other than nums[i]", "without dividing", "O(n) time". Each answer needs everything on its left and everything on its right: a Prefix-Suffix Scan, one pass from each end.

CONSTRAINTS & BOUNDS

2≤N≤1052 \le N \le 10^52≤N≤105 and −30≤nums[i]≤30-30 \le nums[i] \le 30−30≤nums[i]≤30. Multiplying the other N−1N - 1N−1 numbers again for every index is up to 101010^{10}1010 multiplications; two passes are 2×1052 \times 10^52×105. Every prefix and suffix product is guaranteed to fit in 32 bits. The two passes read the array front to back, then back to front, so they stream well and stay cache-friendly; the only stored array is res, the output.

FAANG PRODUCTION TRAPS & EDGE CASES

At scale the risk is the size of the numbers, not the loop. This problem guarantees every prefix and suffix product fits in 32 bits; outside that guarantee, a running product of many values overflows a fixed-width integer, so production code checks the range or uses a wider type.

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