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.
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.
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.
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:
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.
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.
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.
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.
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.
4 questions
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.
Now solve some yourself
Only this topic 0 of 13 solved
- EasyMerge two sorted lists
- EasyRemove repeats from a sorted list
- EasyRemove every node holding a value
- EasyReverse a whole list
- MediumBuild a linked list
- MediumRemove every second child in a circle
- MediumJoin several doubly linked lists into one
- MediumOdd positions first, then even
- MediumSwap every two neighboring nodes
- MediumAdd two numbers stored digit by digit in lists
- MediumRotate a list by k places
- MediumSmaller values before the rest, order kept
- HardReverse the list k nodes at a time
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