0Pricing
DSA Interview Prep · Lesson

Array Basics and In-Place Operations

Review indexing, mutation, and the most common array interview pitfalls such as off-by-one errors and modifying a list while iterating.

Array Basics and In-Place Operations is a free DSA Interview Prep lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the DSA Interview Prep learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Arrays as Contiguous Memory

Under the hood, a Python list is backed by a dynamic array — a contiguous block of memory where elements are stored at consecutive addresses. This layout gives O(1) random access by index: Python computes address = base + index × element_size instantly. Insertions or deletions in the middle require shifting all subsequent elements, costing O(n). This asymmetry is the source of most array interview trade-off discussions.

nums = [10, 20, 30, 40, 50]
# O(1) random access
print(nums[2])       # 30
print(nums[-1])      # 50

# O(1) append (amortised)
nums.append(60)
print(nums)          # [10,20,30,40,50,60]

# O(n) insert at beginning
nums.insert(0, 0)    # shifts all elements right
print(nums)          # [0,10,20,30,40,50,60]

Off-by-One: The Classic Array Bug

Off-by-one errors are the most frequent source of wrong answers in array problems. Python's 0-based indexing means the last valid index is len(arr) - 1. When writing loops, decide whether you need < or <= by checking the boundary condition with the smallest valid input (n=1 or n=2). Always trace your boundary with concrete examples before submitting.

def find_max(nums):
    # Use len(nums)-1 as last index
    max_val = nums[0]              # safe if n >= 1
    for i in range(1, len(nums)):  # start at 1, not 0
        if nums[i] > max_val:
            max_val = nums[i]
    return max_val

print(find_max([3, 1, 4, 1, 5]))  # 5
print(find_max([7]))               # 7  (single element)
# Would crash if we accessed nums[len(nums)]

In-Place Reversal with Two Pointers

Reversing an array in-place uses two pointers starting at opposite ends and swapping inward until they meet. This requires O(1) extra space and O(n) time. The condition left < right (strictly less) ensures correctness for both even and odd lengths — with an odd number of elements the middle element stays in place automatically.

def reverse_inplace(arr):
    left, right = 0, len(arr) - 1
    while left < right:
        arr[left], arr[right] = arr[right], arr[left]
        left  += 1
        right -= 1
    # Space: O(1)  Time: O(n)

a = [1, 2, 3, 4, 5]
reverse_inplace(a)
print(a)  # [5, 4, 3, 2, 1]

b = [1, 2, 3]
reverse_inplace(b)
print(b)  # [3, 2, 1]  middle element unchanged

Rotating an Array In-Place

Rotating an array right by k positions can be done in-place by reversing three segments: reverse the full array, then reverse the first k elements, then reverse the remaining n-k elements. This achieves O(n) time and O(1) space — far better than the O(n) space approach of slicing and concatenating. Always reduce k modulo n to handle k ≥ n.

def rotate(nums, k):
    n = len(nums)
    k %= n  # handle k >= n

    def rev(l, r):
        while l < r:
            nums[l], nums[r] = nums[r], nums[l]
            l += 1; r -= 1

    rev(0, n-1)    # reverse all
    rev(0, k-1)    # reverse first k
    rev(k, n-1)    # reverse rest

a = [1, 2, 3, 4, 5, 6, 7]
rotate(a, 3)
print(a)  # [5, 6, 7, 1, 2, 3, 4]

Removing Elements In-Place

Removing duplicates or target values in-place uses a write pointer that tracks where the next valid element should be written. The read pointer scans forward; when it finds a valid element it copies it to the write position and advances both pointers. This is the core pattern for LeetCode problems like 'remove element', 'remove duplicates from sorted array', and 'move zeroes'.

def remove_element(nums, val):
    write = 0
    for read in range(len(nums)):
        if nums[read] != val:
            nums[write] = nums[read]
            write += 1
    return write  # new length

nums = [3, 2, 2, 3]
new_len = remove_element(nums, 3)
print(nums[:new_len])  # [2, 2]

nums2 = [0, 1, 2, 2, 3, 0, 4, 2]
new_len2 = remove_element(nums2, 2)
print(nums2[:new_len2])  # [0, 1, 3, 0, 4]

Move Zeroes: Read-Write Pointer

Move all zeroes to the end of an array while preserving the order of non-zero elements. The read-write pointer approach places each non-zero element at the write position and then fills the tail with zeroes. An alternative approach swaps zeroes backward, preserving order without a second fill pass. Both are O(n) time, O(1) space.

def move_zeroes(nums):
    write = 0
    # Move all non-zeroes to front
    for read in range(len(nums)):
        if nums[read] != 0:
            nums[write] = nums[read]
            write += 1
    # Fill rest with zeroes
    while write < len(nums):
        nums[write] = 0
        write += 1

a = [0, 1, 0, 3, 12]
move_zeroes(a)
print(a)  # [1, 3, 12, 0, 0]

Square and Sort In-Place

Given a sorted array of integers (possibly negative), return an array of their squares in sorted order. The naive approach squares then sorts: O(n log n). The optimal two-pointer approach exploits the fact that the largest squares come from either end of the sorted input: compare the absolute values of the leftmost and rightmost elements and fill the result from right to left in O(n) time.

def sorted_squares(nums):
    n = len(nums)
    result = [0] * n
    left, right = 0, n - 1
    pos = n - 1  # fill from the right
    while left <= right:
        l_sq = nums[left]  ** 2
        r_sq = nums[right] ** 2
        if l_sq > r_sq:
            result[pos] = l_sq
            left += 1
        else:
            result[pos] = r_sq
            right -= 1
        pos -= 1
    return result

print(sorted_squares([-4, -1, 0, 3, 10]))
# [0, 1, 9, 16, 100]

Finding Pivot and Partition

The Dutch national flag problem partitions an array into three sections (less than, equal to, greater than pivot) in-place using three pointers. This is the key sub-step in quick sort and the solution to LeetCode 'sort colors'. Maintaining the invariant that elements before the low pointer are < pivot and elements after the high pointer are > pivot drives the algorithm.

def sort_colors(nums):
    # Dutch national flag: 0s, 1s, 2s
    low, mid, high = 0, 0, len(nums) - 1
    while mid <= high:
        if nums[mid] == 0:
            nums[low], nums[mid] = nums[mid], nums[low]
            low += 1; mid += 1
        elif nums[mid] == 1:
            mid += 1
        else:
            nums[mid], nums[high] = nums[high], nums[mid]
            high -= 1  # don't advance mid: new nums[mid] unexamined

a = [2, 0, 2, 1, 1, 0]
sort_colors(a)
print(a)  # [0, 0, 1, 1, 2, 2]

Modifying Array Elements While Iterating

You can safely modify element values (e.g., multiply by -1 to mark visited) while iterating, but never change the length of a list during a for loop. A safe encoding trick: temporarily encode two values in a single integer (e.g., sign bit) to simulate an extra boolean per element without allocating extra space. This appears in problems like 'find all numbers that disappeared in an array.'

def find_disappeared(nums):
    # Mark visited by negating the value at the index
    for n in nums:
        idx = abs(n) - 1
        if nums[idx] > 0:
            nums[idx] *= -1  # mark as seen
    # Indices with positive values are missing
    return [i + 1 for i, v in enumerate(nums) if v > 0]

print(find_disappeared([4, 3, 2, 7, 8, 2, 3, 1]))
# [5, 6]  -- O(n) time, O(1) extra space

Array Interview Pattern Checklist

Before coding any array problem, run through this mental checklist:

  • Is the array sorted? (enables two pointers, binary search)
  • Are elements bounded (e.g., 1..n)? (enables index-based tricks)
  • Is in-place required? (read-write pointer or swaps)
  • Do I need all pairs or just one? (affects whether nested loops are acceptable)
  • Edge cases: empty array, single element, all-same values
Answering these questions before writing code saves significant debugging time.

def max_profit(prices):
    # Pattern: single scan, track running minimum
    # Time: O(n), Space: O(1)
    if not prices: return 0  # edge case: empty
    min_price = prices[0]
    max_prof  = 0
    for price in prices[1:]:  # start at index 1
        max_prof  = max(max_prof, price - min_price)
        min_price = min(min_price, price)
    return max_prof

print(max_profit([7, 1, 5, 3, 6, 4]))  # 5
print(max_profit([7, 6, 4, 3, 1]))     # 0

Kadane's Algorithm: Maximum Subarray

Kadane's algorithm finds the maximum-sum contiguous subarray in O(n) time and O(1) space. At each step, decide whether to extend the current subarray or start a new one: current = max(num, current + num). If current + num is less than num alone, the current subarray is dragging us down and we start fresh. Track the global maximum throughout.

def max_subarray(nums):
    current = global_max = nums[0]
    for n in nums[1:]:
        current    = max(n, current + n)  # extend or restart
        global_max = max(global_max, current)
    return global_max

print(max_subarray([-2, 1, -3, 4, -1, 2, 1, -5, 4]))
# 6  (subarray [4, -1, 2, 1])
print(max_subarray([-1, -2, -3]))
# -1  (all negative: take the least negative)

Quick Check

Test your understanding of Data Structures & Algorithms — Coding Interview Prep concepts from this lesson.

Lesson Recap

In this lesson you learned: arrays offer O(1) random access but O(n) insertions and deletions in the middle — knowing this asymmetry guides algorithm choice, the read-write pointer pattern removes elements or moves values in-place in O(n) time with O(1) space, and sign-bit encoding and index-as-mark tricks enable O(1) space solutions to problems that would otherwise require an auxiliary array. Next up we explore prefix sums and running totals.

Frequently asked questions

Is the “Array Basics and In-Place Operations” lesson free?

Yes — the full text of “Array Basics and In-Place Operations” is free to read here on the web, and the DSA Interview Prep course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the DSA Interview Prep course, upgrade to CoddyKit PRO.

What will I learn in “Array Basics and In-Place Operations”?

Review indexing, mutation, and the most common array interview pitfalls such as off-by-one errors and modifying a list while iterating. You practise DSA Interview Prep with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start DSA Interview Prep?

No prior experience is required. DSA Interview Prep on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Array Basics and In-Place Operations” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this DSA Interview Prep lesson?

Yes. Every DSA Interview Prep lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Array Basics and In-Place Operations
  2. Prefix Sums and Running Totals
  3. Two Pointers: Opposite Ends
  4. Two Pointers: Slow and Fast
← Back to DSA Interview Prep