🔗 Linked List - Dynamic Data Structure

🟡 Intermediate Linear DS 75 min read

🎯 What is a Linked List?

A Linked List is a linear data structure where elements (called nodes) are stored in non-contiguous memory locations. Each node contains data and a pointer/reference to the next node. Unlike arrays, linked lists can grow and shrink dynamically.

🔗 Visual Structure

[Head] → [10|*] → [20|*] → [30|*] → [40|null]
          Node       Node       Node       Node
          data+next  data+next  data+next  data+next (null = end)

📂 Types of Linked Lists

Singly Linked List

Each node has data + next pointer. Traversal only forward.

[A|*]→[B|*]→[C|*]→null
Doubly Linked List

Each node has data + next + prev pointers. Bidirectional traversal.

null←[A|*|*]⇄[B|*|*]⇄[C|*|*]→null
Circular Linked List

Last node points back to first. No null references.

[A|*]→[B|*]→[C|*]→[A]↺
Circular Doubly

Combination: doubly linked + circular.

[A|*|*]⇄[B|*|*]⇄[C|*|*]⇄[A]↺

💻 PHP Implementation - Singly Linked List

<?php
class Node {
    public int $data;
    public ?Node $next;

    public function __construct(int $data) {
        $this->data = $data;
        $this->next = null;
    }
}

class LinkedList {
    private ?Node $head = null;
    private int $size = 0;

    // Insert at beginning - O(1)
    public function insertFirst(int $data): void {
        $newNode = new Node($data);
        $newNode->next = $this->head;
        $this->head = $newNode;
        $this->size++;
    }

    // Insert at end - O(n)
    public function insertLast(int $data): void {
        $newNode = new Node($data);
        if ($this->head === null) {
            $this->head = $newNode;
        } else {
            $current = $this->head;
            while ($current->next !== null) {
                $current = $current->next;
            }
            $current->next = $newNode;
        }
        $this->size++;
    }

    // Delete first node - O(1)
    public function deleteFirst(): ?int {
        if ($this->head === null) return null;
        $data = $this->head->data;
        $this->head = $this->head->next;
        $this->size--;
        return $data;
    }

    // Delete by value - O(n)
    public function delete(int $data): bool {
        if ($this->head === null) return false;
        if ($this->head->data === $data) {
            $this->head = $this->head->next; $this->size--;
            return true;
        }
        $current = $this->head;
        while ($current->next !== null && $current->next->data !== $data) {
            $current = $current->next;
        }
        if ($current->next !== null) {
            $current->next = $current->next->next;
            $this->size--; return true;
        }
        return false;
    }

    // Search - O(n)
    public function search(int $data): bool {
        $current = $this->head;
        while ($current !== null) {
            if ($current->data === $data) return true;
            $current = $current->next;
        }
        return false;
    }

    // Reverse (in-place) - O(n)
    public function reverse(): void {
        $prev = null; $current = $this->head;
        while ($current !== null) {
            $next = $current->next; // Save next
            $current->next = $prev; // Reverse link
            $prev = $current;       // Move prev forward
            $current = $next;       // Move current forward
        }
        $this->head = $prev;
    }

    // Detect cycle (Floyd's Algorithm) - O(n)
    public function hasCycle(): bool {
        $slow = $this->head; $fast = $this->head;
        while ($fast !== null && $fast->next !== null) {
            $slow = $slow->next;
            $fast = $fast->next->next;
            if ($slow === $fast) return true;
        }
        return false;
    }

    // Find middle (Tortoise & Hare) - O(n)
    public function findMiddle(): ?int {
        $slow = $this->head; $fast = $this->head;
        while ($fast !== null && $fast->next !== null) {
            $slow = $slow->next;
            $fast = $fast->next->next;
        }
        return $slow?->data;
    }

    // Display - O(n)
    public function display(): void {
        $current = $this->head;
        while ($current !== null) {
            echo $current->data . " → ";
            $current = $current->next;
        }
        echo "null\n";
    }

    public function getSize(): int { return $this->size; }
}

// Usage
$list = new LinkedList();
$list->insertLast(10);
$list->insertLast(20);
$list->insertLast(30);
$list->insertFirst(5);
$list->display(); // 5 → 10 → 20 → 30 → null

$list->reverse();
$list->display(); // 30 → 20 → 10 → 5 → null

echo "Middle: " . $list->findMiddle(); // 20 (or 10 for even)

📊 Operations Complexity

Operation Singly LL Doubly LL Array Winner
Access by Index O(n) O(n) O(1) Array
Insert at Head O(1) O(1) O(n) LL
Insert at Tail O(n) O(1)* O(1) LL/Array
Delete at Head O(1) O(1) O(n) LL
Search O(n) O(n) O(n) Tie
Memory per element Data + 1 ptr Data + 2 ptrs Data only Array

📝 Reverse Linked List - Dry Run

Initial: 1 → 2 → 3 → null

Iter prev current next After reverse link
Start null 1 - -
1 1 2 2 null ← 1
2 2 3 3 null ← 1 ← 2
3 3 null null null ← 1 ← 2 ← 3

Result: 3 → 2 → 1 → null ✅

🐘 PHP SPL Linked List

// PHP provides SplDoublyLinkedList (doubly linked list)
$list = new SplDoublyLinkedList();
$list->push('A');          // Add to end
$list->push('B');
$list->unshift('X');       // Add to beginning
$list->add(1, 'Y');        // Insert at index 1

foreach ($list as $value) {
    echo $value . ' ';     // X, Y, A, B
}

$list->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO);
foreach ($list as $value) {
    echo $value . ' ';     // B, A, Y, X (reverse/LIFO)
}

💼 Interview Questions

Array: fast random access, cache-friendly. Linked List: fast insertions/deletions at ends, dynamic size. Use array when you need random access; use LL when you do many inserts/deletes.

Floyd's Cycle Detection (Tortoise & Hare): Use two pointers - slow moves 1 step, fast moves 2 steps. If they meet, cycle exists. O(n) time, O(1) space. To find cycle start: reset one pointer to head, move both at same speed until they meet.

Use a Min Heap. Push head of each list. Pop smallest, add to result, push next of popped node. O(N log K) where N = total nodes, K = number of lists. Much better than O(N*K) pairwise merging.

📝 Quick Quiz

Q1: What is the time complexity to access the 5th element in a singly linked list?

Q2: Inserting at the head of a linked list is:

Q3: Floyd's algorithm detects ______ in a linked list.

Q4: In a doubly linked list, each node has how many pointers?

Q5: Reversing a linked list in-place requires ____ space.

📋 Cheat Sheet

Insert Head O(1) Create node, point to old head
Insert Tail O(n) Traverse to end, attach (O(1) with tail pointer)
Delete Head O(1) Move head to next node
Reverse O(n) 3-pointer technique (prev, curr, next)
Cycle Detection O(n) Floyd's Tortoise & Hare
Find Middle O(n) Slow/fast pointer