Singly Linked Lists — Build from Scratch
📖 Concept
A singly linked list is a sequence of nodes where each node contains a value and a pointer to the next node. The last node points to null.
Why linked lists matter:
- O(1) insertion/deletion at known positions (vs O(n) for arrays)
- No pre-allocated memory — grows/shrinks dynamically
- Foundation for stacks, queues, LRU cache, and many advanced structures
Structure:
Head → [10|→] → [20|→] → [30|→] → [40|null]
Key operations & complexity:
| Operation | Complexity |
|---|---|
| Access by index | O(n) — must traverse from head |
| Search | O(n) — must check each node |
| Insert at head | O(1) — redirect head pointer |
| Insert at tail | O(n) or O(1) with tail pointer |
| Delete at head | O(1) — redirect head to next |
| Delete by value | O(n) — find the node first |
Arrays vs Linked Lists:
- Access: Array O(1) vs LL O(n) — arrays win
- Insert/Delete at start: Array O(n) vs LL O(1) — linked lists win
- Memory: Arrays are contiguous (cache-friendly) vs LL scattered (cache-unfriendly)
- Size: Arrays fixed/resize vs LL dynamic
🏠 Real-world analogy: A conga line at a party. Each person (node) holds the shoulders (pointer) of the person in front. To add someone at the front, they just grab the current leader. To find the 5th person, you count from the front.
💻 Code Example
1// NODE CLASS2class ListNode {3 constructor(val, next = null) {4 this.val = val;5 this.next = next;6 }7}89// SINGLY LINKED LIST — Full implementation10class SinglyLinkedList {11 constructor() {12 this.head = null;13 this.size = 0;14 }1516 // O(1) — Insert at head17 prepend(val) {18 this.head = new ListNode(val, this.head);19 this.size++;20 }2122 // O(n) — Insert at tail23 append(val) {24 const node = new ListNode(val);25 if (!this.head) { this.head = node; }26 else {27 let curr = this.head;28 while (curr.next) curr = curr.next;29 curr.next = node;30 }31 this.size++;32 }3334 // O(1) — Delete head35 deleteHead() {36 if (!this.head) return null;37 const val = this.head.val;38 this.head = this.head.next;39 this.size--;40 return val;41 }4243 // O(n) — Delete by value44 delete(val) {45 if (!this.head) return false;46 if (this.head.val === val) { this.deleteHead(); return true; }47 let curr = this.head;48 while (curr.next) {49 if (curr.next.val === val) {50 curr.next = curr.next.next;51 this.size--;52 return true;53 }54 curr = curr.next;55 }56 return false;57 }5859 // O(n) — Search60 contains(val) {61 let curr = this.head;62 while (curr) {63 if (curr.val === val) return true;64 curr = curr.next;65 }66 return false;67 }6869 // O(n) — Print70 print() {71 const vals = [];72 let curr = this.head;73 while (curr) { vals.push(curr.val); curr = curr.next; }74 console.log(vals.join(" → ") + " → null");75 }76}7778// COMMON INTERVIEW: Reverse a linked list — O(n) time, O(1) space79function reverseList(head) {80 let prev = null, curr = head;81 while (curr) {82 const next = curr.next; // Save next83 curr.next = prev; // Reverse pointer84 prev = curr; // Move prev forward85 curr = next; // Move curr forward86 }87 return prev; // New head88}8990// TypeScript version91class ListNodeTS<T> {92 val: T;93 next: ListNodeTS<T> | null;94 constructor(val: T, next: ListNodeTS<T> | null = null) {95 this.val = val;96 this.next = next;97 }98}99100// Find middle node — Fast & Slow pointers101function findMiddle<T>(head: ListNodeTS<T> | null): ListNodeTS<T> | null {102 let slow = head, fast = head;103 while (fast && fast.next) {104 slow = slow!.next;105 fast = fast.next.next;106 }107 return slow;108}109110// Detect cycle — Floyd's algorithm111function hasCycle<T>(head: ListNodeTS<T> | null): boolean {112 let slow = head, fast = head;113 while (fast && fast.next) {114 slow = slow!.next;115 fast = fast.next.next;116 if (slow === fast) return true;117 }118 return false;119}
🏋️ Practice Exercise
Practice Problems (Easy → Hard):
- Insert a node at a given position in a linked list
- Delete the nth node from the end in one pass
- Reverse a singly linked list (iterative AND recursive)
- Detect if a linked list has a cycle
- Find the middle node of a linked list in one pass
- Merge two sorted linked lists
- Check if a linked list is a palindrome
- Remove duplicates from a sorted linked list
- Find the intersection point of two linked lists
- Add two numbers represented as linked lists
⚠️ Common Mistakes
Losing reference to the head — always save the head before traversing
Not handling edge cases: null head, single node, deleting head node
Forgetting to update size counter after insertions/deletions
Off-by-one errors when traversing —
while (curr.next)vswhile (curr)Not using a dummy/sentinel node for operations that might change the head
💼 Interview Questions
🎤 Mock Interview
Practice a live interview for Singly Linked Lists — Build from Scratch