DSA Linked Lists — Fundamentals Visualized
Learn linked list fundamentals — nodes, head pointer, next links, traversal, vs arrays, with interactive step visualizer and complexity analysis.
Introduction
Linked lists store elements as nodes chained by pointers — unlike
arrays, memory is not contiguous. Each node holds a value and a reference to the next node;
head is the entry point.
This guide covers:
- Node structure — value + next pointer
- Interactive visualizer — head, tail, traversal
- vs arrays — trade-offs at a glance
- When to choose — dynamic size, frequent head inserts
Quick index
| # | Section |
|---|---|
| 1 | How linked lists work |
| 2 | Interactive visualizer |
| 3 | Full implementation |
| 4 | vs Arrays |
| 5 | When to use |
| 6 | Real-life examples |
1. How linked lists work
head → [10 | next] → [20 | next] → [30 | null]| Concept | Meaning |
|---|---|
| Node | { value, next } object |
| head | Reference to first node |
| tail | Last node (next = null) |
| Traversal | Follow next until null |
class Node {
constructor(value) {
this.value = value
this.next = null
}
}
const head = new Node(10)
head.next = new Node(20)
head.next.next = new Node(30)2. Interactive visualizer
Linked List — Fundamentals
Head pointer, nodes, next links, and traversal — the building blocks of every linked list variant.
Structure
Each node stores a value and a pointer (next) to the following node.
Singly linked node
| class Node { | |
| constructor(value) { this.value = value; this.next = null } | |
| } | |
| let head = new Node(10) | entry point |
head is a reference to the first node — not the whole list.
Access i
O(n)
Insert head
O(1)
Space
O(n)
3. Full implementation
class Node {
constructor(value) {
this.value = value
this.next = null
}
}
class LinkedList {
constructor() {
this.head = null
this.tail = null
}
prepend(val) {
const node = new Node(val)
if (!this.head) this.tail = node
node.next = this.head
this.head = node
}
append(val) {
const node = new Node(val)
if (!this.head) {
this.head = this.tail = node
return
}
this.tail.next = node
this.tail = node
}
toArray() {
const out = []
let cur = this.head
while (cur) {
out.push(cur.value)
cur = cur.next
}
return out
}
}Function calls with output
// ── Demo calls — output shown on the right ──
const list = new LinkedList()
list.prepend(10) // → [10]
list.prepend(5) // → [5, 10]
list.append(20) // → [5, 10, 20]
list.toArray() // → [5, 10, 20]4. vs Arrays
| Operation | Array | Linked List |
|---|---|---|
| Access index i | O(1) | O(n) |
| Insert at head | O(n) shift | O(1) |
| Insert at tail | O(1)* | O(n) or O(1) with tail |
| Memory | Contiguous, cache-friendly | Scattered nodes + pointer overhead |
| Size | Fixed or resize copy | Grows one node at a time |
5. When to use
| Use linked lists when | Prefer arrays when |
|---|---|
| Frequent head inserts/deletes | Random access by index |
| Unknown or highly variable size | Cache locality matters |
| Implementing stack/queue adjacency | Binary search needed |
6. Real-life examples
| Real-world structure | Linked list role |
|---|---|
| Music playlist | Each song node points to next — add/remove tracks without shifting |
| Browser history (back stack) | Each page visit is a node — push/pop at head |
| Undo/redo in editors | Action nodes chained — traverse for undo stack |
| Hash map collision chains | Buckets use singly linked lists when keys collide |
| Task queue in job schedulers | FIFO queue built on linked nodes — O(1) enqueue |
Music playlist
Each song is a node — insert or remove tracks by rewiring pointers, no array shift.
head points to first node — O(1) start playback.
Real-world implementation
class SongNode {
constructor(title) { this.title = title; this.next = null }
}
class Playlist {
constructor() { this.head = null; this.current = null }
addNext(title) {
const node = new SongNode(title)
if (!this.head) { this.head = this.current = node; return }
node.next = this.current.next
this.current.next = node
}
playNext() {
if (this.current?.next) this.current = this.current.next
return this.current?.title ?? null
}
toArray() {
const out = []; let cur = this.head
while (cur) { out.push(cur.title); cur = cur.next }
return out
}
}Full working code for the scenario above.
Function calls with output
| // ── Function calls with output ── | |
| const playlist = new Playlist() | |
| playlist.head = new SongNode("Song A") | → ["Song A"] |
Side output shows return value after each call — step through to trace execution.
Summary
Linked lists trade random access for flexible size and O(1) head insertion. Master nodes and pointers here before types and operations.
Next reads: Linked Lists in Memory · Linked List Types
Subscribe to my newsletter
Stay up to date and get notified when I share new contents.
No spam ever, unsubscribe anytime