Linked Lists

A linked list is a chain of nodes where each node holds a value and a pointer to the next node. Unlike arrays, elements are scattered in memory — you can’t jump to the nth element without walking from the head.

Why It Matters

Linked lists teach pointer manipulation, which is foundational for understanding trees, graphs, and memory management. They also appear in language runtimes and OS kernels.

Types

TypeStructureUse
Singly linkednode → node → node → NoneSimple, one direction
Doubly linkedNone ← node ⇄ node ⇄ node → NoneTraverse both ways
Circularnode → node → node → (back to head)Round-robin, buffers

Operations

OperationSinglyDoubly
Access by indexO(n)O(n)
Insert at headO(1)O(1)
Insert at tailO(n) or O(1) with tail ptrO(1)
Delete at headO(1)O(1)
Delete by valueO(n)O(n) search + O(1) remove
SearchO(n)O(n)

Code Example

class Node:
    __slots__ = ('val', 'next')
    def __init__(self, val, next_node=None):
        self.val = val
        self.next = next_node
 
class LinkedList:
    def __init__(self):
        self.head = None
        self._size = 0
 
    def prepend(self, val):
        self.head = Node(val, self.head)
        self._size += 1
 
    def append(self, val):
        if not self.head:
            self.head = Node(val)
        else:
            cur = self.head
            while cur.next:
                cur = cur.next
            cur.next = Node(val)
        self._size += 1
 
    def delete(self, val):
        if not self.head:
            return
        if self.head.val == val:
            self.head = self.head.next
            self._size -= 1
            return
        cur = self.head
        while cur.next:
            if cur.next.val == val:
                cur.next = cur.next.next
                self._size -= 1
                return
            cur = cur.next
 
    def find(self, val):
        cur = self.head
        while cur:
            if cur.val == val:
                return cur
            cur = cur.next
        return None
 
    def __len__(self):
        return self._size
 
    def __iter__(self):
        cur = self.head
        while cur:
            yield cur.val
            cur = cur.next
 
# Usage
ll = LinkedList()
ll.append(1)
ll.append(2)
ll.append(3)
assert list(ll) == [1, 2, 3]
assert len(ll) == 3
ll.delete(2)
assert list(ll) == [1, 3]

Arrays vs Linked Lists

ArrayLinked List
MemoryContiguous (cache-friendly)Scattered (cache-hostile)
Access by indexO(1)O(n)
Insert at frontO(n)O(1)
Memory overheadNone (just data)Pointer per node

In practice, arrays (Python lists) are almost always faster due to cache effects, even for insert-heavy workloads. Use linked lists when you need O(1) insert/delete at known positions (e.g., LRU cache with a doubly linked list + hash table).