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.
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)]All lessons in this course
- Array Basics and In-Place Operations
- Prefix Sums and Running Totals
- Two Pointers: Opposite Ends
- Two Pointers: Slow and Fast