Data Structures & Algorithms25 min readBeginner

Arrays & Strings

Contiguous memory, indices, and the patterns that solve 80% of array problems: two pointers and sliding window.

Arrays at the hardware level

An array is a block of contiguous memory holding a fixed number of equally-sized elements. Because every element is the same size and they're laid out next to each other, you can compute the address of element i in constant time: `base + i * size`. That's why array indexing is O(1).

Python's `list` and JavaScript's `Array` are dynamic arrays — they grow automatically when full, by allocating a bigger block (typically 2x) and copying. This makes `append` amortized O(1): most appends are O(1), but every so often you pay an O(n) copy.

Inserting or removing in the MIDDLE is O(n) because every element after the insertion point has to shift. Avoid this in tight loops.

Two-pointer technique

Many array problems can be solved by maintaining two indices that move toward or alongside each other. This often turns a naive O(n²) solution into O(n).

👫
Real-life analogy — Two friends searching a sorted shelf
One starts at the left, one at the right. They walk toward each other, comparing their books at each step. They cover the whole shelf in HALF the time of one person.
Two Sum on a sorted array
Watch two indices crawl across the array, replacing what would be an O(n²) double loop.
L
1
0
3
1
4
2
5
3
7
4
R
11
5
target=9. L=1, R=11, sum=12
Reverse an array in place
Watch two indices crawl across the array, replacing what would be an O(n²) double loop.
i
1
0
2
1
3
2
4
3
5
4
6
5
j
7
6
Two pointers from both ends
# Reverse an array in place
def reverse(s):
    i, j = 0, len(s) - 1
    while i < j:
        s[i], s[j] = s[j], s[i]
        i += 1
        j -= 1
    return s

print(reverse([1, 2, 3, 4, 5]))   # [5, 4, 3, 2, 1]
# Two Sum on a SORTED array — O(n)
def two_sum_sorted(nums, target):
    left, right = 0, len(nums) - 1
    while left < right:
        s = nums[left] + nums[right]
        if s == target:
            return [left, right]
        elif s < target:
            left += 1     # need bigger sum, move left up
        else:
            right -= 1    # need smaller sum, move right down
    return []

print(two_sum_sorted([1, 3, 4, 5, 7], 9))   # [1, 4]

Sliding window

A sliding window is a pair of pointers [left, right] defining a contiguous range. The right pointer expands the window; the left contracts it when some condition is violated. Every element is visited at most twice — O(n) overall.

Longest substring without repeating characters
Watch two indices crawl across the array, replacing what would be an O(n²) double loop.
LR
a
0
b
1
c
2
a
3
b
4
c
5
b
6
b
7
Window "a" (length 1). Best so far: 1

Use it for any "find the longest/shortest contiguous subarray with property X" problem.

javascript
function longestUnique(s) {
  const lastSeen = new Map();
  let best = 0;
  let start = 0;

  for (let i = 0; i < s.length; i++) {
    if (lastSeen.has(s[i]) && lastSeen.get(s[i]) >= start) {
      // collision INSIDE the current window — shrink from the left
      start = lastSeen.get(s[i]) + 1;
    }
    lastSeen.set(s[i], i);
    best = Math.max(best, i - start + 1);
  }
  return best;
}
console.log(longestUnique("abcabcbb"));    // 3 ("abc")
console.log(longestUnique("bbbbb"));        // 1

Strings are arrays

Strings in JS and Python are immutable — every modification creates a new string. If you're building a long string in a loop, prefer a list/array of pieces and join at the end.

# Bad — O(n²) due to string copies
result = ""
for x in items:
    result += str(x)

# Good — O(n)
result = "".join(str(x) for x in items)
💡 Tip
When you see \"contiguous subarray\" or \"substring\", reach for sliding window. When you see \"sorted array\" or \"pair that sums to X\", reach for two pointers.