Linked lists

A linked list gives up the array's instant lookups in exchange for inserts and deletes that move nothing: every item knows where the next one is, so changing the order is a matter of redirecting an arrow.

3 examples15 to practiceRead · 7 min
The idea

A linked list is a chain of nodes, each pointing to the next

A linked list stores each item in its own small record, a node, which holds two things: the value, and a reference to the next node (its next). The first node is the head. The last node's next is nothing, which is how you know the list has ended.

head381nothingnextnextnexthead381nothingnextnextnext

The list 3, 8, 1. Only the head is known directly; every other node is found by following next.

Unlike an array's items, nodes don't have to sit together in memory. Each one can be wherever there was room when it was created, because the order is stored in the arrows, not in the addresses.

In the pseudocode on these pages, Node(x) makes a new node holding x whose next is nothing, and node.value and node.next read or change its two fields.

The cost

Reaching an item means walking from the head

An array finds item i with one calculation, because item i is at a known offset from the start. A linked list has no such shortcut: the only way to the fourth node is through the first three. Reading the item at position i takes i steps, and finding a value takes up to n: both O(n).

That walk is the price of everything that follows. It is also why linked-list code is full of loops like this one, which stops at the node holding a value:

The payoff

Once you are at the right node, inserting or deleting changes two arrows

Inserting after a node moves no other item. You make the new node, point it at the node that came next, and point the current node at it:

The order of those two lines matters. If you pointed the current node at the new one first, the only reference to the rest of the list would be overwritten, and the rest of the list would be lost.

Deleting is one arrow: point the node before the one to remove past it. Nothing refers to the removed node any more, so the language reclaims it (or, in C and C++, you free it yourself). Because you need the node before, the walk stops one node early:

The rule

A linked list makes inserting after a node you hold, or removing the node after it, O(1), and reaching a node O(n). An array is the other way round. Removing the node you hold itself needs the node before it: another walk in a singly linked list, O(1) only in a doubly linked one.

Note the special case at the top of the delete: if the value is in the head, there is no node before it, and the list simply starts one node later. That kind of edge case is where linked-list bugs live.

A classic

Reversing a list turns every arrow around in one pass

Reversing a linked list is a favorite interview question because it is all about not losing the rest of the list. Walk the list once with three references: prev (the part already reversed), node (the one being turned around) and ahead (where the rest of the list starts, saved before the arrow changes). The walkthrough reverses only a stretch in the middle, so it also has to stitch both ends back in: the node before the stretch, and the rest after it.

Every node is visited once and nothing is copied: O(n) time and O(1) extra space. The nodes never move; only their arrows do.

Variants

Two extra references fix the slow ends

A plain list knows only its head, so adding at the end means walking the whole list. Keeping a second reference to the last node, the tail, makes appending O(1).

A doubly linked list gives every node a prev as well as a next. You can then walk backward, and delete a node given only the node itself, because it knows its neighbor on each side. The cost is one more reference per node, and two more arrows to keep right on every change.

381nextprevnextprev381nextprevnextprev

Doubly linked: node 8 can unlink itself by joining its two neighbors to each other.

Many implementations also start the list with a sentinel: a dummy node that holds no value and is always there. Every real node then has a node before it, so the head special case in the delete above disappears.

In practice

Arrays usually win anyway

Operation

Array

Linked list

Read the item at position i

O(1)

O(i)

Find a value

O(n)

O(n)

Insert or delete at the front

O(n)

O(1)

Insert or delete after a node you hold

O(n)

O(1)

Delete the node you hold

O(n)

O(n) singly linked, O(1) doubly linked

Append at the end

O(1) amortized

O(1) with a tail

Extra memory per item

none

one or two references

The table makes linked lists look good, but two costs don't show in Big-O. Each node carries at least one extra reference, and in languages like Java or Python each node is a separate object with bookkeeping of its own. And because nodes are scattered in memory, walking a list jumps around instead of reading memory in order, which modern processors handle much worse than an array scan.

So arrays are the default. Linked lists earn their place when items are added and removed in the middle of a long sequence all the time, through references you already hold, and as a building block inside other structures.

Check yourself

4 questions

1. A linked list holds 1,000 nodes. How many nodes do you pass through to read the 500th item?
2. When inserting a new node after node, why set fresh.next ← node.next before node.next ← fresh?
3. In a singly linked list you hold a reference to a node and want to delete it. What is the problem?
4. An array and a linked list each hold a million numbers. You add them all up. Which is likely faster, and why?
Takeaways

Remember this

  • A linked list is a chain of nodes, each holding a value and a reference to the next; the head is the way in.
  • Reaching a node takes a walk from the head, O(n); inserting after a node you hold, or removing the one after it, changes one or two arrows, O(1).
  • Save the reference you are about to overwrite, or you lose the rest of the list.
  • A tail reference makes appends O(1); a doubly linked list can walk backward and delete a node given only the node.
  • Arrays are still the default: linked lists cost memory per node and scan more slowly in practice.
Practice

Now solve some yourself

Only this topic 0 of 13 solved

  1. Easy
    Merge two sorted lists
    Linked lists
  2. Easy
    Remove repeats from a sorted list
    Linked lists
  3. Easy
    Remove every node holding a value
    Linked lists
  4. Easy
    Reverse a whole list
    Linked lists
  5. Medium
    Build a linked list
    Linked lists
  6. Medium
    Remove every second child in a circle
    Linked lists
  7. Medium
    Join several doubly linked lists into one
    Linked lists
  8. Medium
    Odd positions first, then even
    Linked lists
  9. Medium
    Swap every two neighboring nodes
    Linked lists
  10. Medium
    Add two numbers stored digit by digit in lists
    Linked lists
  11. Medium
    Rotate a list by k places
    Linked lists
  12. Medium
    Smaller values before the rest, order kept
    Linked lists
  13. Hard
    Reverse the list k nodes at a time
    Linked lists

Mixed with topics you have practiced 0 unlocked

    2 more once you have practiced Two pointers, Hash tables & maps. Play one of its examples to the end and solve one of its problems to unlock them.

    Problems open on their own sites (LeetCode, Codeforces, CodeChef, AtCoder, CSES) in a new tab. Tick Solved when you get it accepted there; your ticks decide which mixed problems open next.

    Next

    Stacks & queues

    Continue