Linked lists — implementing in PHP, when to use
Intermediate5 min read·eng-07-002
compare
Concept
Linked list: A sequence of nodes where each node holds a value and a pointer to the next node. Unlike arrays, nodes are not stored contiguously in memory.
Singly linked list: Each node has value and next. Traversal is forward-only.
Doubly linked list: Each node has value, next, AND prev. Bidirectional traversal. Supports efficient O(1) removal when you have a reference to the node.
Operations:
prepend(insert at head):O(1)— just update the head pointer.append(insert at tail):O(1)if you keep a tail pointer,O(n)otherwise.search:O(n)— must traverse from head.delete with reference to node:O(1)for doubly linked list.delete by value:O(n)— must search first.
Linked list vs array:
- Array:
O(1)random access by index.O(n)insert/delete in the middle (must shift elements). - Linked list:
O(n)to reach position k.O(1)insert/delete once you have the reference. - PHP arrays are hash tables — they don't need shifting on insert. Linked lists only win for very specific patterns.
When to use in PHP: Rarely need a linked list in PHP — the array is usually better. Use cases:
- Undo/redo history (doubly linked,
O(1)step forward/back). - LRU Cache (doubly linked list + hash map —
O(1)access and eviction). - Task scheduler with frequent queue manipulation.
SplDoublyLinkedListif you need it.
PHP built-in: SplDoublyLinkedList, SplQueue (FIFO operations on a doubly linked list), SplStack (LIFO).
Code Example
php
<?php
// Manual singly linked list implementation
class ListNode
{
public function __construct(
public int $value,
public ?ListNode $next = null,
) {}
}
class SinglyLinkedList
{
private ?ListNode $head = null;
private int $size = 0;
public function prepend(int $value): void
{
$this->head = new ListNode($value, $this->head);
$this->size++;
}
public function append(int $value): void
{
$node = new ListNode($value);
if ($this->head === null) { $this->head = $node; $this->size++; return; }
$current = $this->head;
while ($current->next !== null) { $current = $current->next; }
$current->next = $node;
$this->size++;
}
public function delete(int $value): bool
{
if ($this->head === null) return false;
if ($this->head->value === $value) { $this->head = $this->head->next; $this->size--; return true; }
$current = $this->head;
while ($current->next !== null) {
if ($current->next->value === $value) {
$current->next = $current->next->next;
$this->size--;
return true;
}
$current = $current->next;
}
return false;
}
public function toArray(): array
{
$result = [];
$current = $this->head;
while ($current !== null) { $result[] = $current->value; $current = $current->next; }
return $result;
}
}
// LRU Cache — doubly linked list + hash map = O(1) get and put
class LRUCache
{
private array $map = [];
private \SplDoublyLinkedList $list;
public function __construct(private int $capacity)
{
$this->list = new \SplDoublyLinkedList();
}
public function get(string $key): mixed
{
if (!isset($this->map[$key])) return null;
$this->moveToFront($key);
return $this->map[$key]['value'];
}
public function put(string $key, mixed $value): void
{
if (isset($this->map[$key])) { $this->map[$key]['value'] = $value; $this->moveToFront($key); return; }
if (count($this->map) >= $this->capacity) { $this->evict(); }
$this->list->push($key);
$this->map[$key] = ['value' => $value];
}
private function moveToFront(string $key): void
{
// Remove and re-add at end (SplDoublyLinkedList push = end)
foreach ($this->list as $index => $k) {
if ($k === $key) { unset($this->list[$index]); break; }
}
$this->list->push($key);
}
private function evict(): void
{
$key = $this->list->shift(); // remove oldest (front)
unset($this->map[$key]);
}
}