The Core Idea
Why Index Access Is Instant
An array stores its elements in one unbroken block of memory, with every element taking up the exact same amount of space. This is precisely why accessing arr[i] is O(1): the computer doesn't need to search for anything โ it simply calculates the memory address as (starting address) + (i ร element size) and jumps directly there. This calculation takes the same amount of time whether i is 0 or 1,000,000.
This contiguous layout is both the array's greatest strength and the source of its main weaknesses โ because everything is packed together with no gaps, inserting a new element in the middle means every subsequent element must physically shift over to make room, and the array can't simply 'grow' in place if its allocated block is already full.
๐ก Memory Trick
Picture a row of identical mailboxes bolted to a wall, numbered 0 through n-1. Finding what's in mailbox #47 takes exactly the same effort as finding mailbox #2 โ you just walk to that exact spot. But if you want to insert a brand-new mailbox in the middle of the row, every mailbox after it has to be physically unbolted and moved one slot over to make space โ the fixed, contiguous layout that makes lookup instant is the same reason insertion is expensive.
The Trade-offs
Where Arrays Win and Lose
1
Access โ O(1)
Reading or writing arr[i] is instant, calculated directly from the starting memory address โ no traversal needed, unlike a linked list.
2
Search (unsorted) โ O(n)
Finding whether a specific value exists (not by index, but by value) requires checking each element in turn, since there's no ordering to exploit โ unless the array happens to be sorted, in which case binary search brings this down to O(log n).
3
Insertion/Deletion in the middle โ O(n)
Adding or removing an element anywhere except the very end requires shifting every subsequent element by one position to close or open the gap, which costs time proportional to how many elements come after the insertion/deletion point.
4
Insertion/Deletion at the end โ O(1) amortized
Appending to (or removing from) the end of a dynamic array is O(1) in the typical case, because no shifting is required โ this is the exception to the general 'arrays are bad at insertion/deletion' rule, and it's the specific operation amortized analysis explains in detail.
Static vs. Dynamic Arrays
Fixed-Size Blocks vs. Automatically Growing Ones
A static array has a fixed size determined when it's created โ if you need more room than that, you must create an entirely new, larger array and copy everything over manually. Many lower-level languages default to static arrays.
A dynamic array (like Python's list, Java's ArrayList, or C++'s vector) automatically handles this resizing for you internally: when its underlying allocated capacity is exhausted, it silently allocates a new, larger block (typically double the size) and copies existing elements into it, all behind the scenes โ the amortized O(1) append that the Amortized Analysis lesson works out in detail is exactly this behavior.
๐ฅ๏ธ Applied Scenario
You're storing a fixed roster of exactly 30 students for a semester-long class, where you frequently need to look up 'student at seat #17' but never add or remove students mid-semester.
1
You recognize the access pattern is dominated by lookups by position ('seat #17'), with essentially zero mid-list insertion or deletion once the semester begins.
2
You choose a plain array (rather than a linked list), since arrays give O(1) access by index โ exactly the operation this use case relies on constantly.
3
Because the roster size (30) is fixed and known in advance and never changes mid-semester, a static array is entirely sufficient โ you don't need the automatic resizing overhead a dynamic array provides.
4
Conclusion: the access pattern (frequent lookups by index, no mid-list structural changes) makes a plain fixed-size array the clearly correct choice, over both a linked list and an unnecessarily flexible dynamic array.
๐ Exam Application
Exam questions commonly ask you to state the time complexity of access, search, insertion, and deletion for arrays, and to compare these directly against linked lists for the same operations. You may also be asked to explain, mechanically, why array access is O(1) โ the expected answer references the direct memory-address calculation from the starting address, index, and element size, not a vague 'because it's stored in order.'
โ ๏ธ Most Common Arrays Mistakes
The most common mistake is treating all insertions/deletions as equally costly โ inserting or deleting at the very end of a dynamic array is O(1) amortized, while doing so anywhere else is O(n); conflating these leads to incorrect complexity claims. Another frequent error is assuming a dynamic array's automatic resizing is 'free' โ while amortized cost per append is O(1), any single append that triggers a resize is a real O(n) operation in that specific moment, which matters for real-time systems with strict per-operation latency requirements.
โ Quick Self-Test
Can you explain, using the memory-address calculation, exactly why arr[i] is O(1) regardless of array size? Can you state which specific array operations are O(1) versus O(n), and explain the one exception where insertion is O(1) instead of O(n)?
โ
โ All Data Structures Lessons