Contiguous memory, O(1) indexing, and the traversal, insertion, rotation and Kadane techniques that power a third of all interview questions.
0 of 8 lessons checked off
Introduction
What it is
An array is a block of elements stored side by side in memory, addressed by index. Python's list is a dynamic array: it keeps a contiguous buffer and grows it automatically.
Because element i lives at a predictable memory offset, reading or writing any index costs O(1) — the defining property everything else trades against.
Why it matters
Arrays are the default container: fastest constant factors, cache-friendly, and the substrate for strings, matrices, heaps, and hash tables.
The cost asymmetry — O(1) reads but O(n) middle insertions — is the reason other structures (linked lists, trees, hash maps) exist at all. Understanding arrays deeply makes every later trade-off obvious.
How it works
Indexing computes an address: base + i × element_size. That's why it is O(1) and why indexes start at 0.
Inserting or deleting anywhere but the end must shift every later element to keep the block contiguous — O(n).
Growth uses doubling: when the buffer fills, allocate roughly twice the space and copy — O(n) occasionally, O(1) amortised per append.
Where it's used
Image pixels, audio samples, database rows in a column store, and every spreadsheet column are arrays — anything scanned in bulk benefits from contiguity.
CPU caches fetch memory in 64-byte lines, so iterating an array is dramatically faster than chasing pointers in a linked structure of the same size.
In interviews
Direct array manipulation: rotate, merge sorted arrays in place, move zeroes, spiral-order a matrix.
As the substrate for patterns: two pointers, sliding window, prefix sums, and Kadane's algorithm all assume O(1) indexing.
Analogy: An array is a row of numbered parking spots: driving straight to spot 37 is instant, but squeezing a new car between spots 3 and 4 means every car behind must move back one space.
Interactive diagram
Watch what really happens when you call list.insert(2, 99) — this is where the O(n) comes from.
40
81
152insert here
163
234
Start
Insert 99 at index 2. Every element from index 2 onward must shift one slot right first.
1 / 5
Lessons in this topic
Check off lessons as you go — your progress is saved in this browser.
One-dimensional arrays and memory
Contiguity, base + offset indexing, dynamic-array growth.
15 min
Array traversal
Forward, backward, enumerate, and index vs value iteration.
10 min
Insert and delete operations
Why middle edits shift elements; end edits are O(1).
20 min
Two-dimensional arrays and matrix traversal
Row-major layout, nested loops, and spiral/diagonal walks.
25 min
Rotation
The three-reversal trick for rotating in place.
20 min
Kadane's algorithm
Maximum subarray sum in one pass by extending or restarting.
25 min
In-place operations
Overwrite-and-shrink, swap-to-end, and the read/write pointer idiom.
20 min
Prefix sums and difference arrays (preview)
Precompute once, answer range queries in O(1) — full topic later.
10 min
Operations
Insert at index
Make room by shifting the suffix one slot right, then write. End-appends skip the shifting entirely.
40
81
152insert here
163
234
Start
Insert 99 at index 2. Every element from index 2 onward must shift one slot right first.
1 / 5
1definsert_at(arr:list[int],index:int,value:int)->None:2"""Insert value at index, shifting later elements right. O(n)."""3arr.append(0)# grow by one slot4foriinrange(len(arr)-1,index,-1):5arr[i]=arr[i-1]# shift right, back to front6arr[index]=value
Time: O(n) — O(1) amortised when appending at the endSpace: O(1) extra
Edge cases
index == len(arr) degenerates to append.
index == 0 shifts every element — the worst case.
Out-of-range index should raise, not silently clamp.
Common mistakes
Shifting front-to-back, which overwrites values before they are copied.
Forgetting that repeated front-inserts in a loop cost O(n²) total.
Delete at index
Overwrite the victim by shifting the suffix one slot left, then shrink. There are no holes in an array.
40
81
992
153
164
235
Mark the victim
Delete index 2 (value 99). Arrays cannot leave holes, so later elements shift left.
1 / 5
1defdelete_at(arr:list[int],index:int)->int:2"""Remove and return arr[index], shifting later elements left. O(n)."""3removed=arr[index]4foriinrange(index,len(arr)-1):5arr[i]=arr[i+1]# shift left, front to back6arr.pop()# drop the duplicated last slot7returnremoved
Time: O(n) — O(1) when deleting the last elementSpace: O(1) extra
Edge cases
Deleting from an empty array must raise IndexError.
Deleting the last index does zero shifting.
If order doesn't matter, swap with the last element and pop: O(1).
Common mistakes
Deleting from a list while iterating it forward — indexes shift under you; iterate backwards or build a new list.
Using remove(value) (O(n) search + O(n) shift) when the index is already known.
Rotate right by k (three reversals)
Reverse the whole array, then reverse the first k and the rest separately — rotation in place with no extra buffer.
Rotate right by k (three reversals)
1defrotate(nums:list[int],k:int)->None:2"""Rotate right by k steps, in place. O(n) time, O(1) space."""3n=len(nums)4ifn==0:5return6k%=n# rotating by n is a no-op78defreverse(lo:int,hi:int)->None:9whilelo<hi:10nums[lo],nums[hi]=nums[hi],nums[lo]11lo,hi=lo+1,hi-11213reverse(0,n-1)# [1,2,3,4,5,6,7] -> [7,6,5,4,3,2,1]14reverse(0,k-1)# k=3 -> [5,6,7,4,3,2,1]15reverse(k,n-1)# [5,6,7,1,2,3,4]
Time: O(n)Space: O(1)
Edge cases
k larger than n — always take k % n first.
Empty array or k == 0: return immediately.
k % n == 0 leaves the array unchanged.
Common mistakes
Popping and re-inserting one element k times: O(n·k).
Allocating a second array when the prompt says in place.
Kadane's algorithm (maximum subarray)
One pass, two numbers: the best sum ending here (extend or restart) and the best sum anywhere.
-20
11
-32
43
-14
25
16
-57
48
Initialise
current and best both start at the first value, -2. current tracks the best subarray ending here.
current
-2
best
-2
1 / 10
1defmax_subarray(nums:list[int])->int:2"""Maximum sum over all contiguous subarrays. O(n) / O(1)."""3best=current=nums[0]4forxinnums[1:]:5current=max(x,current+x)# restart or extend6best=max(best,current)7returnbest
Time: O(n)Space: O(1)
Edge cases
All-negative input: the answer is the largest single element — never return 0 unless empty subarrays are allowed.
Single element: both best and current are that element.
Common mistakes
Initialising best to 0, which breaks on all-negative arrays.
Resetting current to 0 instead of to the current element.
Complexity analysis
Operation
Best
Average
Worst
Space
Access by index
O(1)
O(1)
O(1)
—
Search (unsorted)
O(1)
O(n)
O(n)
O(1)
Append at end
O(1)
O(1)
O(n) on resize
—
Insert / delete at front or middle
O(1) at end
O(n)
O(n)
O(1)
Rotate in place
O(n)
O(n)
O(n)
O(1)
Kadane / single scan
O(n)
O(n)
O(n)
O(1)
Python implementation
Production-quality code with type hints, validation, and docstrings.
A dynamic array from scratch (what Python's list does underneath)
1classDynamicArray:2"""Agrowablearraybuiltonafixed-sizebuffer,mirroring3CPython'slist:O(1)index,amortisedO(1)append."""45def__init__(self)->None:6self._capacity=47self._size=08self._buffer:list[object]=[None]*self._capacity910def__len__(self)->int:11returnself._size1213def_check_index(self,index:int)->None:14ifnot0<=index<self._size:15raiseIndexError(f"index {index} out of range for size {self._size}")1617defget(self,index:int)->object:18self._check_index(index)
What interviewers expect you to know
Properties you must state cold
O(1) random access comes from address arithmetic on contiguous memory.
Middle insert/delete is O(n) because contiguity forbids holes.
Python list append is amortised O(1) via capacity doubling.
Slicing copies: nums[a:b] costs O(b−a) time and space.
Frequent follow-ups
"Can you do it in place?" — the interviewer wants O(1) extra space: think reversal tricks, read/write pointers, or swapping to the end.
"What if the array is sorted?" — sortedness unlocks binary search and two pointers; always ask or note it.
"How would you handle streaming input?" — Kadane-style running state that never re-reads old elements.
How to explain arrays in an interview
Frame trade-offs as read-heavy vs edit-heavy: arrays win when reads dominate; linked structures win when middle edits dominate.
When you write nums[i], say what invariant i maintains ('everything left of write is finalised') — invariants are what interviewers grade.
Common mistakes
Off-by-one on boundaries
range(n) visits 0..n−1; the last index is n−1; range(a, b) excludes b. Trace one tiny example on paper before running.
Mutating while iterating
Removing items from a list inside `for x in arr` skips elements as indexes shift. Iterate a copy, iterate backwards, or build a new list.
O(n) operations disguised as O(1)
insert(0, x), pop(0), `in`, and slicing are all linear. Counting them as constant flips your complexity answer from right to wrong.
Returning 0 for all-negative Kadane
best must start at nums[0], not 0 — the maximum subarray of [-3, -1, -2] is −1.
Row/column confusion in 2-D arrays
matrix[r][c]: r selects the row list, c the element. Building grids with [[0]*cols]*rows aliases every row to the same list — use a comprehension.
Practice problems
Hand-picked from the 355-problem DSA Atlas. Reveal hints and solutions progressively; mark and bookmark as you solve.
6 questions across concepts, code output, complexity, and scenarios. Answer all, then submit for explanations.
Frequently asked questions
Is a Python list the same as an array?
It's a dynamic array of object references. The indexing and shifting behaviour matches classic arrays; the differences (heterogeneous elements, reference indirection) affect constants, not Big O.
When should I choose a linked list over an array?
When you do many insertions/deletions at known positions (especially the front) and few random reads. If reads dominate — the common case — arrays win.
What's the fastest way to delete when order doesn't matter?
Swap the victim with the last element and pop: O(1). Interviewers love this trick in 'remove element' style questions.
Summary & cheat sheet
Key takeaways
Arrays trade O(n) middle edits for O(1) reads — the foundational trade-off of data structures.
Shift right back-to-front to insert; shift left front-to-back to delete.
Rotation = three reversals; deletion-without-order = swap-and-pop.
Kadane: current = max(x, current + x); best tracks the answer. O(n), handles negatives correctly if seeded from nums[0].
Formulas & cheat sheet
address(i) = base + i × element_size
Shifts for insert at i: n − i; for delete at i: n − i − 1
Rotate right k: reverse(0, n−1), reverse(0, k−1), reverse(k, n−1) with k %= n
Interview checklist
I can implement insert/delete with explicit shifting loops.
I can rotate in place and explain why k %= n first.