← All posts
DSAVisualizers

Arrays, Linked Lists, and Big O, Visualized

Why array access is O(1), what a reference actually is, and how fast O(2ⁿ) gets out of hand. Plus three interactive visualizers to play with.

4 min read AI-generated

A lot of data structures content explains things with words when you really need to see memory move. So I built three interactive pages: one for arrays, one for linked lists, and one for Big O. This post is the short version of what each one shows. The links at the bottom are where you actually click around.

Arrays: one unbroken block

An array stores every element in a single contiguous block of memory. No gaps. That one fact explains almost everything about how arrays perform.

  • O(1) access. Elements are evenly spaced, so the address of any element is one formula: base_address + index * element_size. No searching.
  • Cache-friendly. When you read arr[0], the CPU pulls nearby memory into cache. arr[1] and arr[2] are right next door, so they're already there.
  • O(n) insertion. Insert in the middle and every element after it has to shift right.
  • Fixed size. To grow, you allocate a bigger block and copy everything. Dynamic arrays like ArrayList and Python's list hide this, but it still happens. That's why append is O(1) amortized.

Arrays are also the foundation for most other structures. A stack is an array with a top pointer. A queue can be a circular array with head and tail pointers. A hash map is an array of buckets. A heap is a complete binary tree stored in an array, with children of i at 2i+1 and 2i+2.

The arrays visualizer lets you click any cell and see the address math, then insert and watch every element after it shift.

Linked lists: nodes scattered everywhere

Linked list nodes aren't stored next to each other. They can live anywhere in memory. Each node just holds the address of the next one.

That's the part that confuses people, so here's the key idea: a variable doesn't contain an object. It contains a memory address pointing to where the object lives on the heap. When you write head.next = new Node(3), you're storing the new node's address inside head.next.

class Node {
    int val;
    Node next;
    Node(int val) { this.val = val; }
}

Node head = new Node(5);
head.next = new Node(12);
head.next.next = new Node(8);

Node curr = head;
while (curr != null) {
    System.out.println(curr.val);
    curr = curr.next;
}
OperationArrayLinked list
Access by indexO(1)O(n)
Insert / delete at headO(n)O(1)
Insert at endO(1)*O(n)
SearchO(n)O(n)
MemoryContiguousScattered

* Amortized, for dynamic arrays. Insert at end is O(n) for a singly linked list with only a head pointer.

Where linked lists show up in real systems: LRU caches (a doubly linked list plus a hashmap gives O(1) access and eviction), undo/redo history, music playlists, and file systems that chain blocks on disk.

Big O: how things scale

Big O is about growth. The cleanest way to feel it is to look at roughly how many operations each class takes at a million elements:

ComplexityExampleOps at n = 1M
O(1)Array access, hash lookup1
O(log n)Binary search~20
O(n)Single loop1,000,000
O(n log n)Merge sort, heap sort~20,000,000
O(n²)Nested loops, bubble sort1,000,000,000,000
O(2ⁿ)Naive recursive Fibonacci, subsetsDon't even try

A few things that are worth internalizing:

  • Binary search halves the problem every step. A million elements takes about 20 steps.
  • O(n log n) is the best you can do for comparison-based sorting. Arrays.sort and Python's sort() are both in that class.
  • Brute-force two sum is O(n²). At 100k elements that's 10 billion operations. A hashmap gets it to O(n).
  • Naive recursive Fibonacci branches twice per call. n = 40 takes seconds, n = 50 takes minutes. Memoization brings it to O(n).
# O(2^n): every call branches into two more
def fib(n):
    if n <= 1:
        return n
    return fib(n - 1) + fib(n - 2)

# Fix: @lru_cache or a dp array gets you O(n)

The Big O page has a slider for n so you can watch the curves pull apart. It makes the difference between n log n and n² obvious in a way the table can't.

Drafted by AI from my notes and guides, reviewed by me.