Linked Lists
pointer updates, dummy nodes, reversal, cycle detection
Linked lists teach pointer thinking: how to rewire structure safely, how to reason about nodes instead of indices, and how to keep invariants stable while the shape of the data changes.
1. Definition
What is a linked list?
A linked list is a linear data structure made of nodes. Each node stores a value and a reference to another node. In a singly linked list, each node points to the next node; in a doubly linked list, each node points both forward and backward. Unlike an array, the elements are not stored by index in one contiguous block â the structure is held together by references.
Why does it exist?
A linked list exists to make local structural changes cheap. If you already know the node before the insertion or deletion point, rewiring pointers can be done in O(1). That is the main trade-off: you gain flexible structural updates but lose direct index access.
Where does it fit?
Linked lists matter when the problem is about pointer updates, node insertion or removal, reversing order, splitting or merging lists, detecting cycles, or building comfort with reference manipulation. In interviews, linked lists are less about production container choice and more about precision â they reveal whether you can change structure without losing information.
Intuition
Think of a linked list like train cars. Each car knows the next car. If you want to remove one car, you do not shift every later car left like in an array â you reconnect the previous car to the next car. That is why local edits are cheap. If someone asks for the car at position 17, however, you must walk through the chain. That is why random access is slow.
Java context
In Java, linked list interview problems usually use a custom ListNode class. The point is rarely the JDK LinkedList API â the point is to show that you understand references, null handling, and structural invariants. A minimal node looks like this:
class ListNode {
int value;
ListNode next;
ListNode(int value) {
this.value = value;
}
ListNode(int value, ListNode next) {
this.value = value;
this.next = next;
}
}
Why this topic is hard for many people
Many candidates do not fail because linked lists are conceptually deep. They fail because one small pointer update is done in the wrong order. That means this topic is really about disciplined state handling.
2. Core Concepts
2.1 Node-based structure
A linked list is not one big object with indexed slots â it is a chain of node objects. Each node contains payload data, one or more links, and sometimes auxiliary metadata.
2.2 Singly vs doubly linked list
| Variant | Node links | Strength | Cost |
|---|---|---|---|
| Singly linked list | next |
Simple, lightweight | No direct backward movement |
| Doubly linked list | prev, next |
Easier removal and reverse traversal | More memory, more pointer maintenance |
For interview problems, singly linked lists are the most common.
2.3 Head and tail thinking
The head is the first node. Sometimes you also track a tail, which helps when appending matters. But tail references also increase the number of invariants you must maintain. Examples: empty list â head == null (maybe tail == null); one-element list â head == tail; multi-element list â tail.next == null.
2.4 Traversal is sequential
To reach the node at position k, you walk k steps. That means access by index is O(n), searching by value is O(n), and many solutions begin with a traversal phase. This is one reason linked lists are often worse than arrays for raw scanning workloads.
2.5 Insertion and deletion by rewiring
The real power is pointer rewiring.
If prev points to the node before current, then deleting current is conceptually:
prev.next = current.next;
If you want to insert newNode after prev, the safe pattern is:
newNode.next = prev.next;
prev.next = newNode;
The order matters.
If you overwrite prev.next too early, you can lose the rest of the list.
2.6 Dummy node pattern
A dummy node is a fake node placed before the real head.
It simplifies edge cases.
Why it helps:
- deleting the real head becomes a normal deletion,
- inserting before the first real node becomes ordinary,
- and your logic stops branching on âis this the first node?â.
This is one of the most useful linked list interview patterns.
2.7 Reversal thinking
Reversal is a classic exercise because it forces you to preserve information while rewiring links.
The standard iterative invariant is:
previousholds the already reversed prefix,currentholds the next node to process,nextNodetemporarily preserves the remainder.
Without that saved nextNode, the rest of the list can disappear from reach.
2.8 Fast and slow pointers
Two-pointer linked-list problems often use:
- slow pointer moving one step,
- fast pointer moving two steps.
This pattern helps with:
- cycle detection,
- middle node finding,
- linked list splitting,
- and palindrome-related preparation.
2.9 Cycle detection
Floydâs tortoise-and-hare algorithm is the standard cycle pattern.
If a cycle exists, a faster pointer eventually laps the slower pointer.
If the fast pointer reaches null, the list is acyclic.
The beauty is that it uses O(1) extra space.
2.10 Complexity profile
| Operation | Complexity | Notes |
|---|---|---|
| Access by index | O(n) |
Must walk node by node |
| Search by value | O(n) |
Sequential scan |
| Insert at front | O(1) |
Head update |
| Delete front | O(1) |
Head update |
| Insert after known node | O(1) |
Rewiring only |
| Delete after known previous node | O(1) |
Rewiring only |
| Append without tail | O(n) |
Must walk to end |
| Append with tail | O(1) |
If tail invariant is maintained |
2.11 Reference semantics matter
With arrays, âpositionâ is often the main identity.
With linked lists, the node reference itself matters.
That changes the debugging mindset.
Instead of saying âI am at index iâ, you often say:
- â
currentis the node being processedâ, - â
previousis the end of the solved regionâ, - â
slowpoints to the candidate middleâ.
2.12 Recognition signals
Linked-list reasoning should trigger when you see:
- custom node classes,
- next/prev references,
- reversal,
- remove the
n-th node, - detect a cycle,
- merge two sorted lists,
- reorder a chain,
- split a list,
- or constant-space structural edits.
3. Practical Usage
When to use linked-list thinking in interviews
Use linked-list thinking when the input is explicitly node-based.
Typical signals:
- âGiven the head of a linked list...â
- âReverse the listâ
- âRemove duplicates from a sorted linked listâ
- âDetect whether the list contains a cycleâ
- âMerge two sorted linked listsâ
In these problems, trying to force index-based thinking usually makes the solution worse.
Typical linked-list patterns
Common families include:
- dummy node for head-safe mutation,
- fast/slow pointer for middle and cycle tasks,
- iterative reversal,
- recursive reversal,
- merge with tail builder,
- partition with multiple temporary lists,
- and read-pointer/write-pointer style rewiring.
When linked lists are a poor production choice
In many real Java applications, arrays or ArrayList are better.
Why:
- better cache locality,
- lower object overhead,
- simpler APIs,
- and better constant factors.
So interview linked lists are often more about algorithmic discipline than container recommendation.
When NOT to use linked-list thinking
Do not prefer linked lists when the problem needs:
- random index access,
- binary search,
- cache-friendly scans on primitives,
- or frequent lookups by key.
In those cases, arrays, hash-based structures, or trees are usually better fits.
Java-specific advice
For stack-like behavior, prefer Deque over the legacy Stack class.
For queue behavior, prefer ArrayDeque when possible.
For interview linked-list problems, however, use an explicit ListNode class because that is what the problem models.
Pair-programming angle
A strong explanation sounds like this:
- âIâll use a dummy node so deleting the head becomes an ordinary case.â
- âIâll keep
previous,current, andnextNodebecause reordering the updates can lose the tail.â - âThe invariant is that everything before
previousis already correctly reversed.â
That is much stronger than just saying "I know the linked-list trick."
Related LeetCode problems
- LeetCode #206 â Reverse Linked List â three-pointer reversal with invariant discipline.
- LeetCode #21 â Merge Two Sorted Lists â dummy node + comparison walk.
- LeetCode #141 â Linked List Cycle â fast/slow pointer cycle detection.
- LeetCode #19 â Remove Nth Node From End of List â two-pointer gap technique.
- LeetCode #876 â Middle of the Linked List â fast/slow to find midpoint.
4. Code Examples
Basic Example
This example builds a small list and traverses it.
public class LinkedListTraversalExample {
static class ListNode {
int value;
ListNode next;
ListNode(int value) {
this.value = value;
}
}
public static void printList(ListNode head) {
ListNode current = head;
while (current != null) {
System.out.print(current.value + (current.next != null ? " -> " : ""));
current = current.next;
}
System.out.println();
}
public static void main(String[] args) {
ListNode head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);
printList(head);
}
}
Why it matters:
- it shows sequential traversal,
- it shows node wiring,
- and it reinforces that linked lists are walked, not indexed.
Advanced Example
This example reverses a singly linked list iteratively.
public class ReverseLinkedListExample {
static class ListNode {
int value;
ListNode next;
ListNode(int value) {
this.value = value;
}
}
public static ListNode reverse(ListNode head) {
ListNode previous = null;
ListNode current = head;
while (current != null) {
ListNode nextNode = current.next;
current.next = previous;
previous = current;
current = nextNode;
}
return previous;
}
}
Key invariant:
previousis the reversed prefix,currentis the next unprocessed node,nextNodeprotects the remainder.
Common Pitfall
This is a classic bug.
public class BrokenReverseExample {
static class ListNode {
int value;
ListNode next;
ListNode(int value) {
this.value = value;
}
}
public static ListNode brokenReverse(ListNode head) {
ListNode previous = null;
ListNode current = head;
while (current != null) {
current.next = previous;
previous = current;
current = current.next;
}
return previous;
}
}
Why it fails:
- after
current.next = previous, the original next node is gone, current = current.nextnow walks backward,- and the unreversed suffix is lost.
The fix is to save nextNode before overwriting current.next.
Another practical pattern: removing with a dummy node
public class RemoveValueExample {
static class ListNode {
int value;
ListNode next;
ListNode(int value) {
this.value = value;
}
}
public static ListNode removeAll(ListNode head, int target) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode current = dummy;
while (current.next != null) {
if (current.next.value == target) {
current.next = current.next.next;
} else {
current = current.next;
}
}
return dummy.next;
}
}
This is a strong interview example because it removes special-case logic for deleting the head.
5. Trade-offs
| Aspect | Details |
|---|---|
| ⥠Performance | Great for local rewiring once you already have the node or previous node; poor for indexing and cache-heavy scans |
| đŸ Memory | Higher overhead than arrays because each node is a separate object and stores references |
| đ§ Maintainability | Clean when invariants are explicit; fragile when pointer updates are implicit or badly ordered |
| đ Flexibility | Excellent for structural edits such as splice, reverse, split, merge, and partition |
Performance nuance
Big-O alone is not enough.
A linked list may offer O(1) insertion after a known node.
But if you first need an O(n) traversal to find that node, the full operation is still effectively linear.
Memory nuance
Each node creates object overhead.
That is one reason linked lists often underperform arrays in real Java systems even when the asymptotic story sounds attractive.
Readability nuance
Linked-list code becomes readable when variable names match the invariant.
Names like previous, current, nextNode, slow, fast, dummy, and tail are not cosmetic.
They are part of the safety mechanism.
6. Common Mistakes
- Losing the rest of the list â The most common bug is overwriting
current.nextbefore saving the old next node. Save first, rewire second. - Ignoring head edge cases â Deleting or inserting at the front often breaks naive logic. Use a dummy node when mutation starts near the head.
- Advancing the wrong pointer â In deletion loops, moving
currentafter removal can skip nodes or cause inconsistent behavior. - Comparing the wrong thing â Sometimes the problem wants node identity, not just node value. Be clear about whether equality means same reference or same payload.
- Forgetting null boundaries â
fast.next.nextis unsafe unless you first prove bothfastandfast.nextare non-null. - Mixing array intuition with list reality â There is no cheap index jump. If your reasoning keeps using âthe middle indexâ without traversal, the model is wrong.
- Breaking tail invariants â If you maintain a tail pointer, you must update it consistently after append, delete, and merge operations.
Debugging tip
When a linked-list bug appears, draw four things only:
- node values,
- arrows,
- variable names,
- and the next update.
That is usually enough.
7. Deep Dive
Why linked lists often lose to arrays in Java
This surprises many people.
Theoretical O(1) insertion sounds powerful.
But in practice:
- arrays are cache-friendly,
- primitive arrays avoid object overhead,
- and CPU-friendly contiguous scans are extremely fast.
A linked list creates many small heap objects.
Following references is much worse for locality.
So the âbest asymptotic structureâ can still be slower in production.
Why interviewers still love linked lists
Because linked lists expose three valuable skills:
- reference discipline,
- invariant-driven reasoning,
- and edge-case control.
If you can safely reverse, split, merge, and detect cycles, you usually understand state transitions much better.
Dummy nodes are more than a trick
A dummy node is a design pattern for removing special cases.
That is the deeper lesson.
Good engineering often means reshaping the problem so the main loop becomes uniform.
Fast/slow pointer insight
Fast/slow pointer solutions are elegant because they encode structure into motion.
Examples:
- if fast moves twice as quickly, meeting implies a cycle,
- if fast reaches the end, slow approximates the middle,
- if one pointer starts later, relative movement can align distances.
This is algorithmic reasoning, not memorization.
Recursive linked-list solutions
Some problems also have elegant recursive forms.
But recursion adds call-stack space.
That may be fine for clarity.
It may also violate the strongest extra-space target.
In interviews, say that explicitly.
Real-world scenario
Suppose you are pair-programming on a âremove duplicates from sorted listâ problem.
A strong approach is not just code.
It is explanation:
- the list is sorted,
- duplicates are adjacent,
- one pass is enough,
- rewiring is local,
- and the main risk is skipping nodes after deletion.
That kind of narration is often what separates a solid candidate from a merely fast coder.
8. Interview Questions
Q: Why is a linked list bad for random access? â A: Because nodes are reached by following references sequentially. There is no direct index-to-address jump like in an array, so access by position is O(n).
Q: When is insertion in a linked list really O(1)? â A: When you already have the node before the insertion point, or the exact place to rewire. If you must first traverse to find that position, the overall operation includes that traversal cost.
Q: Why is a dummy node so useful? â A: It removes special handling for the real head. Deleting or inserting at the front becomes structurally identical to the general case.
Q: How does Floydâs cycle detection work? â A: A slow pointer moves one step and a fast pointer moves two. In a cycle, the fast pointer eventually catches the slow pointer. Without a cycle, the fast pointer reaches null.
Q: Why might ArrayList outperform a linked list in Java even when linked-list insertion looks cheaper on paper? â A: Because arrays and dynamic arrays have much better locality and lower object overhead. Real performance depends on constant factors and memory behavior, not only asymptotic complexity.
9. Glossary
| Term | Definition |
|---|---|
ListNode |
A node type used in linked-list problems, typically holding a value and a next reference |
| dummy node | A synthetic node placed before the head to simplify mutation logic |
| rewiring | Reassigning references so the structure changes without copying all elements |
| fast/slow pointers | Two references moving at different speeds to infer structural properties |
| cycle | A linked-list condition where following next eventually revisits an earlier node |
| sentinel | A special helper node used to simplify boundaries or represent structure edges |
10. Cheatsheet
- Use linked-list thinking when the problem is about nodes, not indices.
- Save
nextNodebefore overwritingcurrent.next. - Use a dummy node when the head may be deleted or replaced.
slow/fastpointers are the standard tools for middle and cycle problems.- Access by index is
O(n), notO(1). - Local rewiring can be
O(1)when the right node is already known. - Arrays often beat linked lists in real Java performance because of locality.
- Good linked-list code names variables after invariants.
- If mutation feels branch-heavy, a dummy or tail builder may simplify it.
- In interviews, explain pointer order before you code it.
đź Games
10 questions