[]

Arrays & Strings

Contiguous memory, O(1) index access. Two pointers & sliding window live here.

Arrays & Strings

Arrays store elements in contiguous memory — O(1) random access, O(n) insert/delete at arbitrary positions.

Static vs Dynamic Arrays

  • Static: Fixed size at creation (C arrays)
  • Dynamic (Python list, Java ArrayList): Resize by doubling when full
    • Append: O(1) amortized — occasional O(n) resize is averaged out over all appends

Complexity

| Operation | Array | Dynamic Array (Python list) | |-------------------|--------|-----------------------------| | Access by index | O(1) | O(1) | | Append | — | O(1) amortized | | Insert at front | O(n) | O(n) | | Delete at index | O(n) | O(n) | | Search (unsorted) | O(n) | O(n) | | Search (sorted) | O(log n) | O(log n) |

String Immutability in Python

Strings are immutable — building with += in a loop is O(n²) because each concatenation copies the whole string. Use list + "".join():

# Bad — O(n^2): each += creates a new string
result = ""
for c in chars:
    result += c

# Good — O(n): build list, join once at the end
parts = []
for c in chars:
    parts.append(c)
result = "".join(parts)

Two Pointer Variations

There are three distinct two-pointer patterns:

1. Opposite ends — sorted array pair problems

def two_sum_sorted(arr, target):
    left, right = 0, len(arr) - 1
    while left < right:
        s = arr[left] + arr[right]
        if s == target:   return [left, right]
        elif s < target:  left += 1
        else:             right -= 1
    return []

2. Different speeds — fast/slow (cycle, middle)

def find_middle(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    return slow

3. Same direction with offset — string comparison when lengths differ

def is_one_edit_away_insert(shorter, longer):
    i = j = diff = 0
    while i < len(shorter):
        if shorter[i] != longer[j]:
            diff += 1
            if diff > 1: return False
            j += 1        # skip one char in longer
        else:
            i += 1; j += 1
    return True

Sliding Window Pattern

def max_sum_subarray(arr, k):
    window_sum = sum(arr[:k])
    best = window_sum
    for i in range(k, len(arr)):
        window_sum += arr[i] - arr[i - k]  # slide
        best = max(best, window_sum)
    return best

Prefix Sum

def subarray_sum(arr, k):
    """Count subarrays summing to k — O(n) with prefix hash."""
    prefix_count = {0: 1}
    total = count = 0
    for x in arr:
        total += x
        count += prefix_count.get(total - k, 0)
        prefix_count[total] = prefix_count.get(total, 0) + 1
    return count

In-Place Matrix Rotation

Rotate NxN matrix 90° clockwise in place — two approaches:

Transpose + reverse each row (clean):

def rotate_90_cw(matrix):
    n = len(matrix)
    # Transpose: swap [i][j] with [j][i]
    for i in range(n):
        for j in range(i + 1, n):
            matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
    # Reverse each row
    for row in matrix:
        row.reverse()

Layer-by-layer 4-cycle (classic in-place):

def rotate_layers(matrix):
    n = len(matrix)
    for layer in range(n // 2):
        last = n - layer - 1
        for i in range(layer, last):
            offset = i - layer
            top = matrix[layer][i]
            # left → top
            matrix[layer][i] = matrix[last - offset][layer]
            # bottom → left
            matrix[last - offset][layer] = matrix[last][last - offset]
            # right → bottom
            matrix[last][last - offset] = matrix[i][last]
            # top → right
            matrix[i][last] = top

Formula: new_row = old_col, new_col = size - old_row - 1 Time: O(n²), Space: O(1)

Character Frequency Counting

from collections import Counter

# Palindrome permutation check
def is_palindrome_permutation(s):
    freq = Counter(c.lower() for c in s if c != ' ')
    return sum(v % 2 for v in freq.values()) <= 1

# Anagram check
def is_anagram(s, t):
    return Counter(s) == Counter(t)

Key Interview Tips

  • Off-by-one errors are the #1 bug — always trace boundary conditions on paper
  • Consider sorting first if order doesn't matter (often unlocks O(n log n))
  • Two-pointer technique eliminates a nested loop → O(n²) → O(n)
  • For in-place 2D rotation: transpose + reverse-rows is the cleanest approach