Crux Read real two-pointer, sliding-window, prefix-sum, and in-place snippets, then predict the output, name the complexity, or spot the bug a test would catch.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at middle altitude — in the sky
◷ 14 min
The pattern is easy to recite and easy to break. Read each snippet the way you would in a review: trace the pointers, check the boundary, and decide whether the loop does what its author thinks it does.
Goal
Practise reading the unit’s patterns in code — predicting output, naming the true complexity, and catching the off-by-one or missing-precondition bug before it ships.
Snippet 1 — the variable-size window
function minSubarrayLen(arr, target) { let left = 0, sum = 0, best = Infinity; for (let right = 0; right < arr.length; right++) { sum += arr[right]; while (sum >= target) { best = Math.min(best, right - left + 1); sum -= arr[left]; left++; } } return best === Infinity ? 0 : best;}// minSubarrayLen([2, 3, 1, 2, 4, 3], 7)
Quiz
Completed
What does this call return, and what is the algorithm's time complexity?
Heads-up [4, 3] = 7 gives length 2, beating [1, 2, 4]. And it is O(n): left only moves forward across the whole run, so the inner loop is amortized over n total moves — not O(n²).
Heads-up Several subarrays reach 7 — e.g. [4, 3]. The function returns 0 only when no window qualifies, which is not the case here.
Heads-up The length is right, but Math.min is O(1) and there is no sorting or halving. The bound is O(n) from forward-only pointer movement.
Snippet 2 — in-place compaction
function removeVal(arr, val) { let write = 0; for (let read = 0; read < arr.length; read++) { if (arr[read] !== val) { arr[write] = arr[read]; write++; } } return write;}// const a = [3, 2, 2, 3]; const k = removeVal(a, 3);
Quiz
Completed
After the call, what is k, and what is the state of the first k cells of a?
Heads-up The write-pointer technique does not clear the tail; it only guarantees a[0..k-1] are the kept values. The trailing cells keep whatever was last written there and are meant to be ignored.
Heads-up k is the write pointer, not the loop count. It advances only when arr[read] !== val, so it ends at 2 — the number of kept elements.
Heads-up The condition keeps elements that are NOT equal to val, so the 3s are dropped and the 2s are kept. a[0..1] = [2, 2].
Snippet 3 — opposite-ends two-sum
function twoSum(arr, target) { let left = 0, right = arr.length - 1; while (left < right) { const sum = arr[left] + arr[right]; if (sum === target) return [left, right]; else if (sum < target) left++; else right--; } return null;}// twoSum([8, 2, 5, 1, 7], 9)
Quiz
Completed
The array [8, 2, 5, 1, 7] is not sorted. A correct pair summing to 9 exists (8 + 1, or 2 + 7). What does this code actually return, and why?
Heads-up Trace it: 8 + 7 = 15 > 9 → right-- ; 8 + 1 = 9 → returns [0, 3]. It returns a pair by luck. The real lesson is that correctness is not guaranteed on unsorted input.
Heads-up The loop guard left < right prevents crossing; it exits cleanly and returns null when no pair is hit. It does not throw.
Heads-up Two pointers gives no such ordering guarantee on unsorted data. Tracing this input, it reaches 8 + 1 = 9 and returns [0, 3] — and on other inputs may miss the pair entirely.
Snippet 4 — prefix-sum range query
function buildPrefix(arr) { const p = new Array(arr.length + 1).fill(0); for (let i = 0; i < arr.length; i++) p[i + 1] = p[i] + arr[i]; return p;}function rangeSum(p, i, j) { return p[j] - p[i - 1]; }// const p = buildPrefix([2, 5, 1, 3, 4]); // p = [0, 2, 7, 8, 11, 15]// rangeSum(p, 1, 3) // intended: arr[1] + arr[2] + arr[3] = 9
Quiz
Completed
buildPrefix is correct, but rangeSum has an off-by-one bug. What does rangeSum(p, 1, 3) return, and what is the fix?
Heads-up Substitute: p[3] − p[0] = 8 − 0 = 8, not 9. The indices are shifted by one because p[k] sums the first k elements — the query needs p[j+1] − p[i].
Heads-up p[0] is defined and equals 0 — buildPrefix fills it. There is no crash; the bug is a silent wrong answer of 8 from mismatched indexing.
Heads-up Prefix sums do not systematically undercount; the formula just has to align with the convention. Here p[3] − p[0] = 8; the fix is p[j+1] − p[i] = 9.
Recap
Reading the patterns in code is where the bugs hide. The variable-size window is O(n) because left only moves forward — the inner while is amortized, not nested. In-place compaction guarantees only a[0..k-1]; the tail is stale by contract. Opposite-ends two-sum is unsound on unsorted input even when it accidentally returns a pair. And prefix-sum queries live or die on the n+1 convention: p[k] is the sum of the first k elements, so the range formula is p[j+1] − p[i]. Trace the pointers and check the boundary before you trust the loop.