Linear

Linked List Fundamentals

A linked list stores elements as a chain of nodes, where each node holds a value and a pointer to the next node. Unlike arrays, elements are scattered across memory โ€” but any node can be inserted or removed in O(1) time once you have a reference to its neighbour, making linked lists the natural complement to arrays.

01
Section One ยท Foundation

What is Linked List?

5 [0] 12 [1] 8 [2] 42 [3] โˆ…

A linked list is a collection of nodes allocated anywhere in memory, each holding two things โ€” a data value and a pointer (reference) to the next node. Because nodes are not contiguous, there is no address formula for direct access: reaching element i requires following pointers from the head node, one hop at a time. This makes access O(n) but makes insertion and deletion at a known position O(1) โ€” no shifting required, just pointer rewiring.

Linked lists come in two main flavours:

Singly Linked

One Direction

Each node stores one pointer โ€” next. Traversal is forward-only. Memory-efficient: one pointer overhead per node.

  • Simple to implement
  • Efficient for stack and queue internals
  • Can only traverse forward
Doubly Linked

Bidirectional

Each node stores two pointers โ€” next and prev. Enables backward traversal and O(1) deletion without needing the predecessor.

  • O(1) delete with just a node reference
  • Enables deque (double-ended queue)
  • 2ร— pointer memory overhead per node
Analogy: A treasure hunt where each clue tells you only where the next clue is hidden. You cannot jump to clue number 7 โ€” you must start at clue 1 and follow every step in sequence. But adding a new clue anywhere in the chain is instant: rewrite two clues to point through the new one.
Key Characteristics
Nodes scattered in heap memory
โ–ถ
No O(1) index access โ€” must traverse
Each node holds a next pointer
โ–ถ
Insert/delete O(1) with a reference in hand
No pre-allocation needed
โ–ถ
Size grows/shrinks dynamically with zero waste
No contiguous layout
โ–ถ
Cache-unfriendly โ€” slower iteration than arrays
Doubly linked adds prev pointer
โ–ถ
O(1) delete & backward traversal
02
Section Two ยท Operations

Core Operations

Traverse
Visits every node from head to tail by following next pointers, accumulating or searching as it goes.
Pseudocode
function traverse(head): current = head while current is not null: visit(current.data) // O(1) per node current = current.next // advance pointer // total: O(n)
Always check for null before accessing .next. The most common linked list crash is dereferencing a null pointer at the tail. Every loop condition should be current != null โ€” not current.next != null โ€” unless you specifically need to stop one node before the end.
Insert Node
Wires a new node into the chain at the head, tail, or a given position by updating exactly two pointers.
Pseudocode
function insertAfter(prevNode, value): newNode = Node(value) newNode.next = prevNode.next // step 1: point new โ†’ successor prevNode.next = newNode // step 2: point prev โ†’ new // O(1) โ€” only two pointer updates function insertAtHead(head, value): newNode = Node(value) newNode.next = head // new node points to old head head = newNode // head now points to new node return head
Order the pointer updates carefully. When inserting, always set newNode.next first, then update the predecessor's pointer. Reversing this order loses the reference to the rest of the list โ€” a very common bug.
Delete Node
Removes a node from the chain by bypassing it โ€” redirecting the predecessor's next pointer to skip over the target node.
Pseudocode
function deleteAfter(prevNode): if prevNode.next is null: return // nothing to delete prevNode.next = prevNode.next.next // bypass target // O(1) with prevNode in hand function deleteByValue(head, target): if head.data == target: return head.next // delete head โ€” O(1) current = head while current.next is not null: if current.next.data == target: current.next = current.next.next // bypass return head current = current.next return head // not found โ€” O(n) search
Finding the node to delete is O(n); the deletion itself is O(1). In a doubly linked list, deletion given only the node reference (no predecessor) is O(1) because node.prev gives you the predecessor directly. In a singly linked list, you must traverse to find it โ€” O(n).
Cycle Detection โ€” Floyd's Algorithm
Uses a slow pointer advancing one step and a fast pointer advancing two steps to detect whether the list contains a cycle in O(n) time and O(1) space.
Pseudocode
function hasCycle(head): slow = head fast = head while fast is not null and fast.next is not null: slow = slow.next // advance 1 step fast = fast.next.next // advance 2 steps if slow == fast: return true // pointers met โ€” cycle exists return false // fast hit null โ€” no cycle
If there is a cycle, slow and fast will always meet. Fast gains one step on slow per iteration. The gap between them decreases by 1 each time, so they are guaranteed to converge inside the cycle โ€” never to "jump over" each other. This is also the basis of finding the cycle entry point (LeetCode 142).
Common Mistake: Losing the head reference. When you write head = head.next to advance, the original head is gone forever โ€” the entire list behind it is orphaned. Always use a separate current pointer for traversal and leave head pointing to the first node at all times.
03
Section Three ยท Visuals

Diagrams & Memory Layout

Singly vs Doubly Linked List Structure
Node anatomy โ€” value compartment + pointer compartment(s)
SINGLY LINKED 10 โ†’ [0] 20 โ†’ [1] 30 โˆ… [2] DOUBLY LINKED โˆ… 10 โ†’ [0] prev next โ† 20 โ†’ [1] โ† 30 โˆ… [2] Singly: 1 pointer/node ยท Doubly: 2 pointers/node
Insert After Node โ€” Pointer Rewiring
Inserting node 99 after node 20 โ€” two pointer updates
BEFORE 10 20 30 [0] [1] [2] โ†“ insert 99 here AFTER 10 20 99 30 [0] [1] [2] new [3] โ‘  new.next = 30   โ‘ก prev.next = new Only 2 pointer updates โ€” O(1) regardless of list length
Floyd's Cycle Detection โ€” Slow & Fast Pointers
Slow pointer (1 step) and fast pointer (2 steps) converge inside the cycle
A B S C cycle start D F E cycle back-edge Slow (1 step) Fast (2 steps) If slow == fast โ†’ cycle detected ยท If fast reaches null โ†’ no cycle
04
Section Four ยท Code

Java Quick Reference

The most common linked list operations using Java's built-in LinkedList โ€” a doubly linked list you'll use in real code and interviews.

Java โ€” LinkedList Core Operations
import java.util.*; LinkedList<Integer> list = new LinkedList<>(); // O(1) โ€” insert at head and tail list.addFirst(10); // [10] list.addLast(30); // [10, 30] list.add(1, 20); // O(n) insert at index โ†’ [10, 20, 30] // O(n) โ€” access by index (must traverse) int val = list.get(1); // 20 int head = list.getFirst(); // 10 โ€” O(1) int tail = list.getLast(); // 30 โ€” O(1) // O(1) โ€” remove from head list.removeFirst(); // removes 10 โ†’ [20, 30] // O(n) โ€” remove by index list.remove(1); // removes 30 โ†’ [20] // O(n) โ€” search boolean has = list.contains(20); // true int idx = list.indexOf(20); // 0
05
Section Five ยท Complexity

Time & Space Complexity

OperationBest CaseAverage CaseWorst CaseSpace
Access by indexO(1) at headO(n)O(n)O(1)
Insert at head / tailO(1)O(1)O(1)O(1)
Insert at indexO(1)O(n)O(n)O(1)
Delete at headO(1)O(1)O(1)O(1)
Delete at index / tail (singly)O(1)O(n)O(n)O(1)
Why these complexities? There is no address formula โ€” reaching index i requires following i pointers from the head. But once you hold a reference to a node's predecessor, the insert or delete is just two pointer assignments โ€” truly O(1) regardless of list length. Tail insertion is O(1) only when a tail pointer is maintained; without it, you must traverse the entire list to find the last node.
Space Complexity Note

A linked list of n nodes uses O(n) space for data, plus O(n) for pointers โ€” one per node for singly linked, two per node for doubly linked. This pointer overhead is the primary memory disadvantage versus arrays. However, there is no wasted capacity: a linked list of size n uses exactly n nodes, with no over-allocation. Auxiliary space for all operations is O(1) except recursion-based reversal which uses O(n) call-stack space.

06
Section Six ยท Decision Guide

When to Use Linked List

โœ“

Use This When

  • Frequent insertions / deletions at known positions โ€” O(1) pointer rewiring beats O(n) array shifts every time.
  • You're building a stack or queue โ€” addFirst / removeFirst (stack) and addLast / removeFirst (queue) are both O(1).
  • Size changes constantly โ€” no resizing, no capacity waste, memory is allocated and freed per node.
โœ—

Avoid When

  • Random access is frequent โ€” every get(i) is O(n); an array reaches the same element in O(1).
  • Cache performance matters โ€” nodes scattered in heap memory miss the CPU cache heavily; arrays iterate 3โ€“5ร— faster in practice.
Comparison vs Alternatives
StructureAccessInsertDeleteBest For
Linked List โ† this oneO(n)O(1)O(1)Frequent inserts/deletes, stack/queue internals
ArrayO(1)O(n)O(n)Random access, cache-friendly iteration
Doubly Linked ListO(n)O(1)O(1) no traversalDeque, LRU cache, browser history
07
Section Seven ยท Interview Practice

LeetCode Problems

Linked list interview problems almost always involve one of four patterns: two pointers (fast/slow for cycle detection and midpoint finding), dummy head node (simplifies edge cases at the list head), in-place reversal (rewiring pointers without extra memory), or merge patterns (combining two sorted lists by pointer comparison).

  • Easy Reverse Linked List โ€” Three-pointer iterative approach: prev, curr, next โ€” then swap curr.next = prev each step.
  • Easy Merge Two Sorted Lists โ€” Use a dummy head node; compare heads and attach the smaller one each step โ€” clean O(n+m) merge.
  • Medium Linked List Cycle II โ€” Floyd's detection finds the meeting point; then reset one pointer to head and advance both one step โ€” they meet at the cycle entry.
  • Medium Remove Nth Node From End โ€” Two-pointer gap trick: advance the fast pointer n steps first, then move both โ€” fast hitting null means slow is at the target.
  • Hard LRU Cache โ€” Combine a HashMap with a doubly linked list โ€” O(1) get and put by using the map for lookup and the list for O(1) move-to-front.
Interview Pattern: When a linked list problem feels complicated, always ask two questions first: "Would a dummy head node eliminate my edge cases?" and "Can slow/fast pointers replace a two-pass traversal?" These two moves handle the majority of medium linked list problems without needing extra data structures.