Linked Lists
A linear data structure where elements are not stored at contiguous memory locations but are linked using pointers.
What is it?
A linked list is a chain of nodes, where each node contains data and a pointer to the next node. A perfect real-world example is your web browser's history. When you navigate from one page to another, a new node is created for the new page, and it points to the previous one. Clicking the "Back" button simply follows the pointer to the previous node. Unlike an array, inserting a new page between two existing pages in your history is incredibly efficient (O(1)). You just need to update the pointers of the surrounding nodes. The downside is that you can't instantly jump to the 10th page in your history; you have to traverse the first 9 nodes to get there (O(n) access time).
Time Complexity
Implementation Example
class Node {
constructor(data) {
this.data = data;
this.next = null;
}
}
class LinkedList {
constructor() {
this.head = null;
}
// Method to add a node at the end
append(data) {
const newNode = new Node(data);
if (!this.head) {
this.head = newNode;
return;
}
let current = this.head;
while (current.next) {
current = current.next;
}
current.next = newNode;
}
}Linked Lists
Linked Lists Deep Dive
Linked lists trade random access for constant-time structural edits, which is ideal for editors, schedulers, and buffering systems.
?But why does this matter?
Pointer-rich structures place more pressure on CPU caches; thoughtful node sizing and pooling prevents GC churn.
Visual Flow
How it actually moves
Insert at head/tail
O(1)Pointer rewiring touches only adjacent nodes.
Random access
O(n)Nodes must be traversed sequentially to find index i.
Deletion
O(1)Given a node reference, removal is immediate after pointer updates.
Insert node after target
Create the new node with the incoming value.
Point the new nodeβs next reference to target.next.
Update target.next to the new node.
If tracking tail pointers, update them when insertion occurs at the end.
The cost is constant because only neighboring pointers are touched.
Watch the best explainer
Data structures: Introduction to Linked List
mycodeschool
The classic linked-list explainer millions of engineers learned from.
Imagine a scavenger hunt
Each clue points you to the next location. You can add a new clue between two existing ones without rewriting the whole list, but you canβt skip directly to clue #50 without following the chain.
Real-world analogy
Music apps queue tracks by linking them so friends can add songs anywhere in the line-up.
Takeaway
Linked lists excel when insert/delete operations happen frequently at arbitrary positions.
Practical applications
β’ Undo/redo stacks in design tools where edits are replayed sequentially.
β’ Music players queuing tracks dynamically from different sources.
β’ Networking buffers (e.g., Linux sk_buff) that splice packets efficiently.
Technical insights
β’ Singly linked lists optimize for append or prepend operations; doubly linked lists enable bi-directional traversal.
β’ Sentinel nodes reduce conditionals by keeping head/tail pointers always valid.
β’ Memory pools or arena allocators mitigate fragmentation caused by per-node allocations.
When to reach for it
β’ Streaming scenarios with unpredictable insert positions
β’ Implementing LRU caches or schedulers
Related topics
- queues
- stacks
- hash-tables
Operations Breakdown
Insert at head/tail
O(1)Pointer rewiring touches only adjacent nodes.
Random access
O(n)Nodes must be traversed sequentially to find index i.
Deletion
O(1)Given a node reference, removal is immediate after pointer updates.
Best practices
β’ Prefer intrusive lists (where objects carry next/prev) to avoid wrapper allocations in systems code.
β’ Batch allocations of nodes to improve spatial locality.
β’ Embed size counters when frequent length queries are expected.
Common pitfalls
β’ Poor cache utilization leads to performance cliffs for traversal-heavy workloads.
β’ Losing track of head/tail after pointer updates introduces hard-to-debug leaks.
β’ Recursive list algorithms risk stack overflows on untrusted input size.
Every kind of Linked Lists
Interviewers rarely say which flavor they mean β they expect you to recognize it from the problem. Each variant below comes with the algorithms it unlocks.
Type 1 of 4
Singly Linked List
β full pageEach node points forward only. Cheap insertion at the head, but you can never look back β which is exactly what the classic tricks exploit.
What to know
- Fast/slow pointers meet inside a cycle (Floyd); resetting one to head finds the cycle start.
- Reversal rewires next pointers one node at a time with three cursors (prev/curr/next).
- A dummy head node removes every "is it the first node?" special case.
Algorithms to reach for
Floydβs cycle detection
O(n)Detect and locate loops without extra memory
Iterative reversal
O(n)Reverse whole list or k-groups in place
Fast/slow middle finding
O(n)Find midpoint in one pass (merge sort on lists)
Merge two sorted lists
O(n + m)Zipper-merge by relinking, no allocation
In the wild: Hash table chaining buckets, immutable functional lists, memory allocator free lists.
Practice problems β hints, approach & solution on their own pages
Type 2 of 4
Doubly Linked List
β full pageNodes point both ways, so any node can delete itself in O(1) once you hold a reference to it β the key to LRU caches.
What to know
- LRU cache = hashmap (key β node) + doubly linked list ordered by recency.
- Sentinel head and tail nodes eliminate all edge cases at both ends.
- Costs one extra pointer per node versus singly linked.
Algorithms to reach for
LRU cache (hashmap + DLL)
O(1) per opO(1) get and put with eviction (LeetCode 146)
O(1) node unlink
O(1)Remove a known node without traversal
In the wild: Browser back/forward history, MRU lists, text editor gap navigation, OS page replacement.
Practice problems β hints, approach & solution on their own pages
Type 3 of 4
Circular Linked List
β full pageThe tail points back to the head. Iteration never "ends", which models anything that goes around forever.
What to know
- Round-robin scheduling walks the circle giving each node a time slice.
- The Josephus problem (survivor in a killing circle) has an O(n) recurrence.
- Termination checks compare against the start node instead of null.
Algorithms to reach for
Round-robin traversal
O(1) per stepFair cyclic scheduling of tasks/players
Josephus elimination
O(n)Find the surviving position in a circle
In the wild: CPU schedulers, multiplayer turn order, token-ring networks, carousel UIs.
Practice problems β hints, approach & solution on their own pages
Type 4 of 4
Skip List
β full pageA sorted linked list with express lanes: each node is promoted to higher levels by coin flips, giving O(log n) expected search without rotations.
What to know
- Level-i lanes skip ~2^i nodes; search drops down a level when the next node overshoots.
- Simpler to implement lock-free than balanced trees β why Redis chose it.
- Expected O(log n) holds with high probability; worst case is O(n) but vanishingly rare.
Algorithms to reach for
Skip list search/insert/delete
O(log n) expectedSorted-set operations with simple code
Range iteration
O(log n + k)Walk bottom lane between two bounds
In the wild: Redis sorted sets (ZSET), LevelDB/RocksDB memtables, concurrent ordered maps.
Practice problems β hints, approach & solution on their own pages
Pseudo Code β’ Insert node after target
Flow Diagram
- Create the new node with the incoming value.
- Point the new nodeβs next reference to target.next.
- Update target.next to the new node.
- If tracking tail pointers, update them when insertion occurs at the end.
The cost is constant because only neighboring pointers are touched.
Hands-on code
Compare how the same idea looks in JavaScript, Python, and Go.
class Node {
constructor(value) {
this.value = value;
this.next = null;
}
}
function insertAfter(target, value) {
if (!target) return null;
const node = new Node(value);
node.next = target.next;
target.next = node;
return node;
}