datarekha

Arrays vs Linked Lists

Two ways to hold a sequence — one as a single contiguous block, the other as scattered nodes joined by pointers. The choice reshapes the cost of every operation.

7 min read Beginner Data Structures & Algorithms Lesson 12 of 32

What you'll learn

  • Why an array gives O(1) jump-to-any-index but pays O(n) to insert in the middle
  • How a linked list splices in O(1) once you are there, but walks O(n) pointers to arrive
  • That Python's list is a dynamic array, not a linked list — fast append, slow insert(0)
  • When to reach for collections.deque instead of a plain list

Before you start

Arrays and linked lists are two answers to the same plain question: how do you keep an ordered run of items in memory?

The answer you pick reshapes the cost of every operation that follows — reading, inserting, deleting. Neither is better in the abstract. The right one depends entirely on what you do most.

Two pictures

Think of an array as a row of numbered lockers, all the same size, bolted to the wall side by side. Want locker 47? Multiply the locker width by 47, add the starting address, and arrive in a single step — that is O(1) access to any index. But to squeeze a new locker between 12 and 13, every locker from 13 onward has to shuffle one place over to open a gap. That shuffling is O(n).

Think of a linked list as a treasure hunt. Each node holds a value and a slip of paper saying “the next item is over at this address.” You start at the head and follow the slips. Want the fifth item? Follow four slips to get there — O(n). But once you are holding the right slip, slipping a new node into the chain is trivial: write one new note, rewire two pointers — O(1), with nothing to shuffle.

Array — one contiguous block, jump to any index12594701234index 2 = base + 2×widthLinked list — scattered nodes, follow the next-pointershead12594
The array reaches index 2 by arithmetic. The list reaches the 4th node only by following three pointers in turn.

Cache locality — the cost Big-O drops

There is a real difference here that Big-O never mentions: cache locality.

Array elements sit at consecutive addresses, with nothing between them. When the CPU fetches element 2, the cache line it pulls in also carries 3, 4, and 5 — so the next few reads are almost free. Linked-list nodes can live anywhere on the heap, so following each pointer is likely a fresh trip to main memory, and those trips are slow. This is why an O(n) scan of a packed array often beats an O(log n) walk through a pointer-chasing tree: the array stays hot in cache, while the tree keeps missing. The linked list’s lovely O(1) splice carries a hidden constant that grows with how scattered its nodes are.

Python’s list is a dynamic array

A common surprise: Python’s list is not a linked list. It is a dynamic array — a contiguous block of pointers.

nums = [10, 20, 30]
nums[1]            # O(1) — direct index arithmetic
nums.append(40)    # O(1) amortised — usually spare room at the end
nums.insert(0, 5)  # O(n) — every element shifts one slot right

Indexing is instant. Appending is amortised O(1): there is normally spare capacity at the end, and when there is not, the list copies itself into a block about 1.125× larger — an O(n) step that happens so rarely the average append stays O(1). Inserting at the front, though, shifts every element, so it is O(n) every time.

The practical linked structure: collections.deque

You will rarely build a raw linked list in Python. When you genuinely need O(1) work at both ends, the standard library hands you collections.deque, a doubly-linked list of small blocks:

from collections import deque

q = deque([1, 2, 3])
q.appendleft(0)   # O(1) at the front
q.append(4)       # O(1) at the back
q.popleft()       # O(1) from the front

The rule of thumb is short:

NeedReach for
Fast indexing, mostly appending at the endlist
O(1) at both ends — a queue or sliding windowcollections.deque
Packed numeric data and vectorised mathnumpy.ndarray

Most Python code uses list. You reach for deque the moment you catch yourself writing list.insert(0, …) or list.pop(0) inside a loop — the silent O(n²) trap from the built-ins lesson.

Practice

Quick check

0/3
Q1What is the cost of nums[k] on a Python list of length n?
Q2A hot loop calls nums.insert(0, x) a million times on a growing list. Total cost?
Q3A linked list's O(1) insert sounds better than an array's O(n) insert. Why can the array still win for small n?

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Practice this in an interview

All questions
When would you use a Python list versus a NumPy array, and what are the performance trade-offs?

Python lists are heterogeneous, pointer-based, and general-purpose. NumPy arrays are homogeneous, stored as contiguous typed memory, and support vectorised operations that run at C speed. For numerical work on more than a few hundred elements, NumPy is almost always faster and more memory-efficient.

What is the difference between a generator and a list, and when should you prefer a generator?

A list materialises all values in memory at once; a generator produces values one at a time on demand, using O(1) memory regardless of the sequence length. Prefer generators for large or infinite sequences, pipelines, and any situation where you do not need random access.

In Python, do variables store values or references to objects?

Python variables are names — they store references (pointers) to objects, not the objects themselves. Assignment binds a name to an object; it never copies the object. Understanding this explains why mutating an object through one name is visible through all other names that reference the same object.

What is the difference between a list and a tuple, and when should you use each?

Lists are mutable sequences; tuples are immutable. Use a tuple when the collection of items is fixed by meaning — coordinates, RGB values, function return values — and a list when the collection will grow, shrink, or be modified in place. Immutability also makes tuples hashable, so they can serve as dict keys or set members.

Related lessons

Explore further

Skip to content