Linked Lists

Easy

A linked list is a sequence of nodes where each node contains data and a pointer to the next node. Unlike arrays, linked lists don't require contiguous memory, making insertions and deletions efficient. However, they sacrifice random access.

Interactive Visualization

Linked List
HeadA
TailD
CurrentA
HeadCurrent
A
nextB
B
nextC
C
nextD
Tail
D
nextnull
null
Output
-
visitStart at head. Current points to A while searching for D.
1 / 5
Key Insight: Linked lists do not allow random access. You must traverse from head until you reach the target.

Time Complexity

OperationAverage
AccessO(n)
SearchO(n)
Insert (at head)O(1)
Insert (at position)O(n)
Delete (at head)O(1)
Delete (at position)O(n)

Key Points

  • Each node has: data + pointer to next node
  • No contiguous memory needed - nodes can be anywhere
  • O(1) insert/delete if you have the node reference
  • O(n) access - must traverse from head
  • Types: singly linked, doubly linked, circular
  • Trade-off: fast insert/delete vs slow random access

Code Examples

Node Structure

// Singly Linked List Node
class ListNode {
  constructor(val) {
    this.val = val
    this.next = null
  }
}

// Create a linked list: 1 → 2 → 3
const head = new ListNode(1)
head.next = new ListNode(2)
head.next.next = new ListNode(3)

// Traverse the list
let current = head
while (current !== null) {
  console.log(current.val)  // 1, 2, 3
  current = current.next
}

Nodes linked by next pointers

Insert at Head O(1)

function insertAtHead(head, val) {
  const newNode = new ListNode(val)
  newNode.next = head
  return newNode  // New head
}

// Before: 1 → 2 → 3
// insertAtHead(head, 0)
// After: 0 → 1 → 2 → 3

// O(1) - just update pointers!
// Compare to array unshift which is O(n)

Insert at head is always O(1)

Insert at Position O(n)

function insertAt(head, val, position) {
  const newNode = new ListNode(val)

  if (position === 0) {
    newNode.next = head
    return newNode
  }

  let current = head
  for (let i = 0; i < position - 1; i++) {
    if (!current) return head
    current = current.next
  }

  newNode.next = current.next
  current.next = newNode
  return head
}

// Must traverse to find position: O(n)
// But the actual insertion is O(1)

Finding position is O(n), insertion is O(1)

Reverse Linked List

function reverseList(head) {
  let prev = null
  let current = head

  while (current !== null) {
    const next = current.next  // Save next
    current.next = prev        // Reverse pointer
    prev = current             // Move prev forward
    current = next             // Move current forward
  }

  return prev  // New head
}

// Before: 1 → 2 → 3 → null
// After:  null ← 1 ← 2 ← 3
// Return: 3 → 2 → 1 → null

// Classic interview question!

Reverse by flipping pointers one by one

Detect Cycle (Floyd's)

function hasCycle(head) {
  let slow = head
  let fast = head

  while (fast !== null && fast.next !== null) {
    slow = slow.next        // Move 1 step
    fast = fast.next.next   // Move 2 steps

    if (slow === fast) {
      return true  // They met - cycle exists!
    }
  }

  return false  // Fast reached end - no cycle
}

// Tortoise and Hare algorithm
// If there's a cycle, fast will "lap" slow
// O(n) time, O(1) space!

Fast pointer catches slow if cycle exists

Find Middle Node

function middleNode(head) {
  let slow = head
  let fast = head

  while (fast !== null && fast.next !== null) {
    slow = slow.next
    fast = fast.next.next
  }

  return slow
}

// 1 → 2 → 3 → 4 → 5
// slow: 1 → 2 → 3 (stops here)
// fast: 1 → 3 → 5 → null

// When fast reaches end,
// slow is at middle!

Fast moves 2x speed, slow ends up at middle

Common Mistakes

  • Losing reference to head (always keep track!)
  • Not handling null/empty list edge cases
  • Creating cycles accidentally during manipulation
  • Forgetting to update both prev and next in doubly linked

Interview Tips

  • Draw the pointers! Visualize before coding
  • Use dummy head node to simplify edge cases
  • Fast/slow pointer technique solves many problems
  • Know trade-offs vs arrays: insert O(1) vs access O(n)

Practice Problems

Related Concepts