0/23in Data Structures & Algorithms
Data Structures & Algorithms

Arrays & Strings

The substrate for almost everything else: indexing, in-place tricks, and the cost of the operations you take for granted.

~14 minLesson 13 of 60
Your progressNot started

Arrays are the default shape of interview input because they are simple to draw and brutal about cost. You can jump to nums[i] instantly, but you pay for anything that forces elements to move.

Strings look similar, but in Python they are immutable. That one fact changes how you build and edit them: repeated concatenation copies; collecting pieces and calling "".join(...) does one final copy.

The core habit is to separate reading from writing. Often one index scans the original data while another writes the compacted result in place.

The mental model

An array stores elements in contiguous slots. If the runtime knows the address of slot 0 and the size of each element reference, it can compute the address of slot i directly. That is why indexing is O(1).

Python’s list is a dynamic array: when it fills up, Python allocates a larger backing array and copies the old references into it. One unlucky append can be O(n), but because the array grows by extra capacity instead of one slot at a time, a long sequence of appends averages to amortized O(1) per append.

nums = []
for x in range(5):
    nums.append(x)       # amortized O(1)

third = nums[2]          # O(1) indexing
nums.insert(1, 99)       # O(n): shifts everything from index 1 onward
nums.pop(1)              # O(n): shifts elements left

For strings, treat each edit as producing a new string:

# Bad for many pieces: each += copies the growing prefix.
out = ""
for part in parts:
    out += part

# Good: collect references, then copy once.
out = "".join(parts)

For in-place array edits, keep a write index:

def keep_positive(nums):
    write = 0
    for x in nums:
        if x > 0:
            nums[write] = x
            write += 1
    return nums[:write]

The read loop sees every element once. The write pointer marks the next slot in the compacted prefix.

Array insert / delete costlength 6
Pick an index, then insert or delete.
selected indexshifted elementsIndexing is O(1), but inserting or deleting in the middle forces every later element to move.

Pattern recognition

Variations

Worked problems

Try to name the cost before opening each solution. The code is short because the pattern is the point.

LeetCode 26Easy
  • Array
  • Write index

Remove Duplicates from Sorted Array

Given a sorted list of numbers, rewrite it so each distinct value appears once at the front. Return the count of distinct values; anything after that prefix does not matter.

Approach. Sorted order groups duplicates together, so the first occurrence of a value is exactly the one that differs from the last value we kept. Let write be the length of the unique prefix. Scan from left to right; whenever nums[i] changes from nums[write - 1], copy it into nums[write] and grow the prefix.

Show solution
def remove_duplicates(nums):
    if not nums:
        return 0

    write = 1
    for read in range(1, len(nums)):
        if nums[read] != nums[write - 1]:
            nums[write] = nums[read]
            write += 1

    return write

Complexity. O(n) time, O(1) extra space. Each element is read once, and the array is rewritten in place.

LeetCode 283Easy
  • Array
  • In-place

Move Zeroes

Rearrange a list so all nonzero values keep their original order at the front and all zeroes move to the end. Do it in place.

Approach. First compact the nonzero values with a write index. After that, every position from write to the end must be zero. This avoids repeated swaps and makes the stability requirement automatic.

Show solution
def move_zeroes(nums):
    write = 0

    for x in nums:
        if x != 0:
            nums[write] = x
            write += 1

    for i in range(write, len(nums)):
        nums[i] = 0

Complexity. O(n) time, O(1) extra space. There are two linear passes, which still collapse to O(n).

LeetCode 238Medium
  • Array
  • Prefix/suffix

Product of Array Except Self

For each index, return the product of every number except the one at that index. Do not use division.

Approach. The answer at i is product of everything to the left times product of everything to the right. Fill ans[i] with the left product during a left-to-right pass. Then walk right-to-left while carrying the right product and multiply it into ans[i].

Show solution
def product_except_self(nums):
    ans = [1] * len(nums)

    left = 1
    for i, x in enumerate(nums):
        ans[i] = left
        left *= x

    right = 1
    for i in range(len(nums) - 1, -1, -1):
        ans[i] *= right
        right *= nums[i]

    return ans

Complexity. O(n) time, O(1) extra space if the output array is not counted. The two running products replace separate prefix and suffix arrays.

The classic pitfall

For strings, the same rule is hiding in a different form: repeated concatenation moves the whole growing prefix again and again. Build a list of pieces, then join once.

Your progressNot started