Arrays and Strings

Arrays

An array is a contiguous block of memory that stores elements of the same type at fixed-size offsets. This layout gives arrays a superpower: O(1) random access — any element can be read or written by computing base_address + index × element_size.

Key Properties

  • Contiguous memory: Elements are stored next to each other, enabling CPU cache prefetching.
  • Fixed or dynamic size: Static arrays have a fixed capacity; dynamic arrays (like JavaScript’s Array, Python’s list, or ArrayList in Java) grow by allocating a new larger block and copying.
  • Indexing: Zero-based in most modern languages.
  • Slice/Subarray: O(k) to extract a contiguous range of k elements.

Common Array Patterns

PatternDescriptionComplexity
Two pointersOne pointer starts at the beginning, another at the end (or both at start with different speeds)O(n)
Sliding windowMaintain a window [left, right] that expands and contracts while tracking a conditionO(n)
Prefix sumPrecompute cumulative sums: prefix[i] = sum(arr[0..i]) for O(1) range sum queriesO(n) build, O(1) query
In-place reversalSwap elements from both ends moving inwardO(n)
Dutch National FlagThree-way partitioning with three pointersO(n)

Prefix Sum

The prefix sum technique transforms an array into a cumulative sum array where prefix[i] is the sum of all elements from index 0 to i. This allows computing sum(arr[l..r]) in O(1) time:

prefix[i] = arr[0] + arr[1] + ... + arr[i]
sum(l, r) = prefix[r] - prefix[l - 1]  // O(1) after O(n) preprocessing

Strings

A string is a sequence of characters. In most modern languages (JavaScript, Python, Java, C#), strings are immutable — once created, they cannot be modified. Any operation that appears to modify a string actually creates a new string.

String Immutability Implications

  • Concatenation in a loop is O(n²): Each + creates a new copy of the entire string.
  • Solution: Use a StringBuilder (Java), StringIO (Python), join (Python/JS), or an array of characters.
  • Character access: O(1) in languages with indexed string access.

Common String Algorithms

AlgorithmDescriptionComplexity
Palindrome checkCompare characters from both endsO(n)
Anagram checkSort both strings and compare, or count character frequenciesO(n log n) or O(n)
Subsequence checkTwo-pointer scan through the longer stringO(n + m)
String reversalTwo-pointer swap on character arrayO(n)
Run-length encodingCompress consecutive repeated charactersO(n)

The StringBuilder Pattern

Instead of:

let s = '';
for (let i = 0; i < n; i++) {
  s += arr[i];  // O(n²): new string allocated each iteration
}

Use:

const parts = [];
for (let i = 0; i < n; i++) {
  parts.push(arr[i]);
}
const s = parts.join('');  // O(n): single allocation

When to Use Arrays vs Linked Lists

OperationArrayLinked List
Random access by indexO(1) ✅O(n) ❌
Insert at end (amortized)O(1) ✅O(n) (O(1) with tail ptr)
Insert at beginningO(n) ❌O(1) ✅
Insert in middleO(n) ❌O(1) (with ref) ✅
Memory overheadLow ✅High (pointers) ❌
Cache localityExcellent ✅Poor ❌
Bidirectional traversalN/AWith doubly linked ✅

Key Takeaways

  1. Arrays are the default choice for sequential data when you need fast reads by position.
  2. Strings are immutable arrays in most languages — never concatenate in a loop.
  3. Two-pointer and sliding window are the most important array interview patterns.
  4. Prefix sums convert O(n) range queries into O(1) lookups.
  5. Linked lists excel where arrays struggle: frequent insertions/deletions at arbitrary positions.