Linked Lists Made Easy

Learn step-by-step with visual explanations and real-world analogies

Easy 25 min read Interactive

Why Should You Care About Linked Lists?

Linked lists power many features you use every day

From music playlists to browser history to undo/redo, linked lists are the backbone of dynamic data management.

Music Playlist

Your music app uses a linked list for playlists! Each song points to the next one. Skip forward? Just follow the pointer. Add a song? Insert a new node!

Browser History

The back and forward buttons in your browser use a doubly linked list. Each page points to both the previous and next page you visited!

Undo/Redo in Apps

Text editors and Photoshop use linked lists for undo/redo. Each action is a node pointing to the previous state!

Photo Viewer

When you swipe through photos, that's a linked list! Each photo knows about the next and previous ones.

Operating System Tasks

Your OS uses linked lists to manage running programs. Each process is a node in the task queue!

Social Media Feed

Instagram and Twitter use linked lists for your feed. New posts are added at the head, and you scroll through the chain!

The Basics: What is a Linked List?

Think of a linked list as a train where each car is connected to the next one. You can only move from one car to the next through the connection. That's exactly how a linked list works!

Linked List = Train
🚂
Engine
10
Car 1
20
Car 2
30
Car 3
NULL

Key Points:

Your First Linked List in Python

linked_list_basics.py
# Think of a Node as a train car with two parts:
# 1. Cargo (data) - what the car carries
# 2. Connector (next) - link to the next car

class Node:
    def __init__(self, data):
        self.data = data    # The cargo this car carries
        self.next = None   # Connection to next car (initially none)

# Create our train cars
engine = Node("Engine")    # First car (HEAD)
car1 = Node(10)            # Second car
car2 = Node(20)            # Third car
car3 = Node(30)            # Fourth car

# Connect the cars together
engine.next = car1   # Engine connects to Car 1
car1.next = car2     # Car 1 connects to Car 2
car2.next = car3     # Car 2 connects to Car 3
# car3.next stays None (end of train)

# Travel through the train
current = engine
while current:
    print(f"Visiting: {current.data}")
    current = current.next   # Move to next car

Common Mistake: Forgetting the NULL Check

When you reach the last node, current.next is None. Trying to access None.data crashes your program!

null_check.py
# ✘ WRONG - This will crash!
current = head
while True:   # Infinite loop!
    print(current.data)
    current = current.next   # Error when current becomes None!

# ✔ CORRECT - Always check if current exists
current = head
while current:   # Stops when current becomes None
    print(current.data)
    current = current.next

Try It Yourself: Build a Linked List

Interactive Linked List Builder

Enter values separated by commas to create your own linked list!

Common Patterns

Pattern 1: Two Pointers (Tortoise and Hare)

Imagine a race where the hare runs twice as fast as the tortoise. This technique helps find the middle or detect cycles!

Finding the Middle Element

Start both pointers at the head. Move the slow pointer 1 step and the fast pointer 2 steps. When the fast pointer reaches the end, the slow pointer is at the middle!

Start: Both at the beginning

🐢🐰
Start
2
3
4
5

Move: Hare moves 2 steps, Tortoise moves 1 step

Result: When hare reaches end, tortoise is at middle!

find_middle.py
def find_middle(head):
    # Handle empty list
    if not head:
        return None

    # Start both at the beginning
    slow = head   # Tortoise
    fast = head   # Hare

    # Hare runs twice as fast
    while fast and fast.next:
        slow = slow.next         # Tortoise: 1 step
        fast = fast.next.next    # Hare: 2 steps

    # Tortoise is now at the middle!
    return slow.data

# Example: List [1,2,3,4,5]
# Hare reaches end, Tortoise at 3 (middle)

Pattern 2: Reversing a Linked List

Like turning a one-way street into the opposite direction!

reverse_list.py
def reverse_list(head):
    # We need three pointers to reverse connections
    prev = None     # Previous node (starts as None)
    current = head    # Current node we're processing

    while current:
        # Save the next node before we change the pointer
        next_temp = current.next

        # Reverse the pointer (point backward instead of forward)
        current.next = prev

        # Move forward
        prev = current
        current = next_temp

    # prev is now the new head
    return prev

# Before: 1 → 2 → 3 → 4 → None
# After:  4 → 3 → 2 → 1 → None

Practice Problems

Easy Problem 1: Count Nodes in a Linked List

Given a linked list, count how many nodes it has. Think: how would you count train cars? Start at the engine and count each car until you reach the end!

Traverse the list from head to tail, incrementing a counter at each node. Stop when you hit None.

def count_nodes(head):
    # Start counting from 0
    count = 0
    current = head

    # Visit each node and count
    while current:
        count += 1
        current = current.next

    return count

# Time: O(n) - visit each node once
# Space: O(1) - only using a counter

Easy Problem 2: Find Last Node

Find the last node in a linked list (the tail). The last node is the one whose next pointer is NULL!

Keep moving through nodes until you find one where .next is None.

def find_last(head):
    # Handle empty list
    if not head:
        return None

    current = head
    # Keep going until we find a node with no next
    while current.next:
        current = current.next

    return current.data

Medium Problem 3: Delete a Node by Value

Remove the first node that contains a specific value. Strategy: find the node BEFORE the one you want to delete, then skip over it!

Handle the special case where the head itself needs to be deleted. Then traverse until you find a node whose .next.data matches the target.

def delete_node(head, value):
    # Special case: deleting the head
    if head and head.data == value:
        return head.next

    current = head
    while current and current.next:
        if current.next.data == value:
            # Skip over the node to delete it
            current.next = current.next.next
            break
        current = current.next

    return head

Advanced Techniques

Detecting Cycles - Floyd's Algorithm

What if the linked list loops back on itself? Use the tortoise and hare!

cycle_detection.py
def has_cycle(head):
    # Empty list has no cycle
    if not head:
        return False

    slow = head   # Tortoise
    fast = head   # Hare

    while fast and fast.next:
        slow = slow.next         # Move 1 step
        fast = fast.next.next    # Move 2 steps

        # If they meet, there's a cycle!
        if slow == fast:
            return True

    # Hare reached the end, no cycle
    return False

# Why it works: In a cycle, the faster runner
# will eventually lap the slower runner!

Doubly Linked Lists

Each node has TWO pointers - to both next AND previous nodes!

doubly_linked.py
class DoublyNode:
    def __init__(self, data):
        self.data = data
        self.next = None     # Points forward
        self.prev = None     # Points backward

# Advantages:
# - Can traverse in both directions
# - Easier deletion (no need to track previous)
# - Used in browser history, undo/redo

Quick Reference

Operation What It Does Time Notes
Insert at Head Add to beginning O(1) Instant - just change one pointer
Insert at Tail Add to end O(n) Must find the end first
Delete First Remove head O(1) Instant - just move head pointer
Search Find a value O(n) Check each node one by one
Access by Index Get nth element O(n) Walk through n nodes

When to Use Linked Lists

Frequent insertion/deletion at beginning, unknown or dynamic size, no need for random access, implementing stacks/queues.

When NOT to Use Linked Lists

Need fast access by index, binary search required, cache performance matters, fixed size is known.

Common Interview Patterns

Two Pointers (find middle, detect cycle), Dummy Node (simplify edge cases), Recursion (reverse, merge lists), Fast & Slow (Floyd's algorithm).