Array & Pointer Patterns
Prefix sums, 2D prefix sums, Difference Arrays for range updates in O(1), Opposite-Direction Two Pointers, and Dynamic Sliding Windows.
Linear array processing is the bedrock of algorithmic problem solving, yet naive multi-loop solutions frequently succumb to quadratic slowdowns. Master five fundamental pointer and accumulator paradigms—Prefix Sums, Difference Arrays, Two Pointers, Sliding Windows, and Floyd's Cycle Detection—that transform intractable scans into optimal time.
1. Executive Summary & Learning Objectives
#This module formalizes optimal linear sequence manipulation techniques, replacing repetitive traversals with analytical precomputation, stateful pointers, and invariant-driven boundary tracking.
By the end of this chapter, you will be able to:
- Accelerate Static Range Queries: Formulate 1D and 2D prefix sums to answer arbitrary range sum queries in time.
- Execute Batch Range Updates: Apply difference arrays to perform multiple offline range additions in time per update, reconstructing the final state in time.
- Eliminate Quadratic Nested Loops: Design inward-converging and same-direction two-pointer strategies that traverse sorted sequences in linear time.
- Implement Resizable Sliding Windows: Maintain running subarray states across fixed-length intervals and variable-length condition boundaries with amortized operations.
- Prove Floyd's Cycle Detection: Derive the mathematical distance relation between head-to-entry and meet-to-entry segments in cyclic pointer structures.
2. Topic 134: Prefix Sum Pattern
#1. The Core Problem
#Given an array of elements, answer range sum queries of the form:
"What is the sum of elements from index to index inclusive?"
- Brute Force: Iterate from to for each query per query, total.
- Prefix Sum Optimization: Precompute cumulative sums in time; answer each query in time and overall.
2. 1D Prefix Sum Mechanics
#Define array of length where , with :
Trace Example:
| Index | 0 | 1 | 2 | 3 | 4 | 5 | |
|---|---|---|---|---|---|---|---|
| Array | — | 3 | 1 | 4 | 1 | 5 | 9 |
| Prefix Sum | 0 | 3 | 4 | 8 | 9 | 14 | 23 |
Query: Sum from to (elements ):
3. 2D Prefix Sum (Submatrix Sum Queries)
#To query the sum of any rectangular submatrix with top-left and bottom-right in time, construct 2D array :
The query sum is computed via the 2D Principle of Inclusion-Exclusion:
| Region Component | Inclusion / Exclusion Rationale |
|---|---|
| Covers total area from origin to bottom-right . | |
| Subtracts redundant rectangle above the submatrix. | |
| Subtracts redundant rectangle to the left of the submatrix. | |
| Re-adds the top-left intersection subtracted twice by previous steps. |
3. Topic 135: Difference Array (Range Update Pattern)
#1. The Dual Problem to Prefix Sums
#Given an initial array of size , perform range additions of the form:
"Add value to all elements from index to index inclusive."
- Brute Force: Loop through indices for every update per update, total.
- Difference Array Optimization: Apply updates in time per operation, reconstructing the final values in a single sweep.
2. Mechanics & Invariants
#Maintain difference array of size , where (with ). For each update tuple :
- (initiates offset from onward)
- (neutralizes offset beyond )
After applying all operations, the cumulative prefix sum of yields the final array :
export function applyRangeUpdates(
n: number,
updates: Array<[number, number, number]>
): number[] {
const diff = new Array(n + 1).fill(0);
for (const [L, R, val] of updates) {
diff[L] += val;
if (R + 1 < n) {
diff[R + 1] -= val;
}
}
const result = new Array(n);
let running = 0;
for (let i = 0; i < n; i++) {
running += diff[i];
result[i] = running;
}
return result;
}4. Topic 136: Two Pointers Pattern
#1. Paradigm & Taxonomy
#The two-pointer technique coordinates two indices moving through a linear sequence, pruning search spaces and converting exhaustive comparisons into scans:
| Direction | Strategy | Movement Rule | Canonical Applications |
|---|---|---|---|
| Inward Convergence | left at , right at | If sum too small, left++; if too large, right-- | Two-Sum in sorted array, Container With Most Water, Palindrome verification |
| Same Direction | fast explores, slow writes | fast scans all entries; slow records valid items | Remove Duplicates in-place, Move Zeroes, Partitioning |
2. Implementation: Two-Sum on Sorted Array
#export function twoSumSorted(
numbers: number[],
target: number
): [number, number] | null {
let left = 0;
let right = numbers.length - 1;
while (left < right) {
const currentSum = numbers[left] + numbers[right];
if (currentSum === target) {
return [left, right];
} else if (currentSum < target) {
left++; // Increase sum by shifting to larger element
} else {
right--; // Decrease sum by shifting to smaller element
}
}
return null;
}5. Topic 137: Sliding Window Pattern
#1. Paradigm
#A sliding window maintains a contiguous range across an array or string. By adding newly entering elements and evicting outgoing elements incrementally, it evaluates subarray properties in total time.
2. Window Types & Templates
#| Window Type | Boundary Rule | Time Complexity | Typical Problem |
|---|---|---|---|
| Fixed Window | Length is constant. Slide right by adding and removing . | Maximum sum subarray of length | |
| Variable Window | Expand greedily. If constraint is violated, increment until invariant holds. | Amortized | Longest substring with at most distinct characters, Minimum size subarray sum |
export function variableSlidingWindow(
nums: number[],
targetSum: number
): number {
let left = 0;
let currentSum = 0;
let minLength = Infinity;
for (let right = 0; right < nums.length; right++) {
currentSum += nums[right]; // Expand window
while (currentSum >= targetSum) {
minLength = Math.min(minLength, right - left + 1);
currentSum -= nums[left]; // Contract window
left++;
}
}
return minLength === Infinity ? 0 : minLength;
}6. Topic 138: Fast & Slow Pointers (Floyd's Cycle Start Proof)
#1. Mathematical Proof of Cycle Entry Detection
#Floyd's Cycle-Finding Algorithm uses two pointers: slow advancing 1 step per cycle, and fast advancing 2 steps.
Let:
- = Distance from
headto the cycle entry node. - = Total perimeter length of the cycle.
- = Distance from cycle entry to the initial meeting point inside the cycle.
When the two pointers collide:
- Distance traversed by
slow: - Distance traversed by
fast: , where represents completed loops.
Since fast travels at double the speed of slow:
2. Collision Theorem
#The distance from head to the cycle entry node () is algebraically equivalent to full loops plus the distance from the collision point to the entry node ().
Therefore, resetting slow to head while keeping fast at the meeting point and advancing both at 1 step per iteration guarantees they will collide at the exact cycle entry node.
interface ListNode {
val: number;
next: ListNode | null;
}
export function detectCycleEntry(head: ListNode | null): ListNode | null {
if (!head || !head.next) return null;
let slow: ListNode | null = head;
let fast: ListNode | null = head;
// Phase 1: Detect cycle existence
while (fast && fast.next) {
slow = slow!.next;
fast = fast.next.next;
if (slow === fast) break;
}
if (slow !== fast) return null; // No cycle
// Phase 2: Find cycle entry node
slow = head;
while (slow !== fast) {
slow = slow!.next;
fast = fast!.next;
}
return slow;
}7. Comparative Pattern Selection Guide
#| Pattern | Input Preconditions | Primary Use Cases | Space Overhead |
|---|---|---|---|
| Prefix Sum | Static array, associative operations (, ) | Cumulative range sum queries, balance points | auxiliary table |
| Difference Array | Offline updates, static evaluation at end | Batch range additions across intervals | difference table |
| Two Pointers | Monotonicity (sorted arrays, unidirectional metrics) | Pair sum matching, in-place partitions | pointers |
| Sliding Window | Contiguous subarrays/substrings, monotonic state changes | Min/max window lengths, substring frequencies | or frequency map |
| Fast & Slow Pointers | Linked structures or cyclic state transitions | Cycle detection, cycle entry, midpoint retrieval | pointers |
References & Academic Attribution
#- Halim, S., Halim, F., & Skiena, S. S. (2020). Competitive Programming 4: The Lower Bound of Programming Contests. CP4 Pte Ltd.
- Laaksonen, A. (2020). Guide to Competitive Programming: Learning and Improving Algorithms Through Contests (2nd ed.). Springer.
- Skiena, S. S. (2020). The Algorithm Design Manual (3rd ed.). Springer.
- Cormen, T. H., Leiserson, C. E., Rivest, R. L., & Stein, C. (2022). Introduction to Algorithms (4th ed.). MIT Press.