Linked List Operations — Insert, Delete & Search Visualized
Master linked list operations — prepend O(1), append, delete, search O(n), pointer rewiring, full implementation with function call output, and interactive step visualizer.
Introduction
Linked list operations boil down to rewiring pointers — prepend at head in O(1), search and delete in O(n) unless you hold a direct node reference. This guide walks through each operation with code and an interactive visualizer.
Quick index
| # | Section |
|---|---|
| 1 | Operation overview |
| 2 | Interactive visualizer |
| 3 | Full implementation |
| 4 | Complexity table |
| 5 | Real-life examples |
1. Operation overview
| Operation | Time | Key idea |
|---|---|---|
| Prepend | O(1) | new.next = head; head = new |
| Append | O(n)* | Walk to tail, link new node |
| Delete | O(n) | Find predecessor, skip target |
| Search | O(n) | Linear scan from head |
*O(1) append if tail pointer is maintained.
2. Interactive visualizer
Linked List Operations
Prepend, append, delete, and search — step through pointer rewiring in O(1) and O(n) cases.
Prepend
Insert at head — O(1). New node.next = head; head = new node.
Prepend O(1)
| list.prepend(5) | → [5, 10, 20] |
| node.next = head; head = node | // rewire head pointer |
Cheapest insert — no traversal needed.
Prepend
O(1)
Append*
O(n)
Delete
O(n)
Search
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
}
delete(val) {
if (!this.head) return
if (this.head.value === val) {
this.head = this.head.next
if (!this.head) this.tail = null
return
}
let cur = this.head
while (cur.next && cur.next.value !== val) cur = cur.next
if (cur.next) {
cur.next = cur.next.next
if (!cur.next) this.tail = cur
}
}
search(val) {
let cur = this.head
while (cur) {
if (cur.value === val) return cur
cur = cur.next
}
return null
}
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) // → toArray(): [10]
list.prepend(5) // → toArray(): [5, 10]
list.append(20) // → toArray(): [5, 10, 20]
list.append(30) // → toArray(): [5, 10, 20, 30]
list.delete(10) // → toArray(): [5, 20, 30]
list.search(20) // → Node { value: 20 }
list.search(99) // → null
list.toArray() // → [5, 20, 30]4. Complexity table
| Operation | Singly (no tail) | Singly + tail | Doubly + tail |
|---|---|---|---|
| Prepend | O(1) | O(1) | O(1) |
| Append | O(n) | O(1) | O(1) |
| Delete head | O(1) | O(1) | O(1) |
| Delete arbitrary | O(n) | O(n) | O(1)* |
*Doubly: O(1) delete if you hold the node reference.
5. Real-life examples
| Operation | Real app example |
|---|---|
| Prepend | Add new notification to top of feed — O(1), no re-render of entire list |
| Append | Add message to chat thread end — O(1) with tail pointer |
| Delete | Remove completed todo item — rewire previous node's next |
| Search | Find user in unsorted follower list — O(n) scan |
| Insert after | Add slide after current in presentation editor |
Todo app feed
Prepend new tasks to top — O(1) with no array shifting.
New notification at top — O(1) prepend.
Real-world implementation
class Node { constructor(val) { this.val = val; this.next = null } }
class TodoList {
constructor() { this.head = null; this.tail = null }
prepend(task) {
const n = new Node(task)
if (!this.head) this.tail = n
n.next = this.head; this.head = n
}
append(task) {
const n = new Node(task)
if (!this.head) { this.head = this.tail = n; return }
this.tail.next = n; this.tail = n
}
delete(id) {
if (!this.head) return false
if (this.head.val.id === id) { this.head = this.head.next; if (!this.head) this.tail = null; return true }
let cur = this.head
while (cur.next && cur.next.val.id !== id) cur = cur.next
if (!cur.next) return false
cur.next = cur.next.next; if (!cur.next) this.tail = cur; return true
}
toArray() { const out = []; let c = this.head; while (c) { out.push(c.val.text); c = c.next } return out }
}Full working code for the scenario above.
Function calls with output
| // ── Function calls with output ── | |
| const list = new TodoList() | |
| list.prepend({ id:1, text:"Buy groceries" }) | → ["Buy groceries"] |
Side output shows return value after each call — step through to trace execution.
Summary
Prepend is the linked list superpower — O(1) with no shifting. Everything else usually needs a walk from head.
Next reads: Linked Lists Fundamentals · DSA Arrays for the contrast
Subscribe to my newsletter
Stay up to date and get notified when I share new contents.
No spam ever, unsubscribe anytime