Linked Lists in Python

A linked list is a linear data structure where elements are stored in nodes, and each node points (links) to the next node using references. Unlike Python lists (dynamic arrays), linked lists do not store elements in contiguous memory. This makes certain operations (especially insertions/deletions in the middle) efficient because you only change links rather than shifting elements.

Key idea: In a linked list, the “order” is maintained by pointers, not by indexes.

When to use linked lists:

  • You do lots of insertions/deletions in the middle of the sequence.
  • You don’t need fast random indexing (because linked lists are slow for index access).

When NOT to use:

  • You need frequent random access like arr[i] (Python lists are better).
  • You want cache-friendly performance (arrays are usually faster in practice).

Introduction to Linked Lists

Singly Linked List

A singly linked list node contains:

  • data
  • next pointer → reference to the next node

Structure:

head -> [data|next] -> [data|next] -> [data|next] -> None

Important points:

  • You can move only forward.
  • Deleting a node requires access to its previous node.

Doubly Linked List

A doubly linked list node contains:

  • data
  • prev pointer → reference to previous node
  • next pointer → reference to next node

Structure:

None <- [prev|data|next] <-> [prev|data|next] <-> [prev|data|next] -> None

Advantages over singly:

  • Can traverse forward and backward.
  • Deletions are easier if you have the node reference.

Disadvantages:

  • Extra memory for prev.
  • Slightly more complex implementation.

Implementing Nodes and Linked Lists in Python

Node Class (Singly)

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

Linked List Class (Core Structure)

class LinkedList:
    def __init__(self):
        self.head = None

Helper: Print / Debug the Linked List

def traverse(self):
    current = self.head
    while current:
        print(current.data, end=" -> ")
        current = current.next
    print("None")

Operations on Linked Lists

Insertion

Insertion is usually efficient because you only change references.

Insert at Beginning (O(1))

def insert_at_beginning(self, data):
    new_node = Node(data)
    new_node.next = self.head
    self.head = new_node

Explanation:

  • New node points to current head
  • Head updates to new node

Insert at End (O(n))

def insert_at_end(self, data):
    new_node = Node(data)
    if self.head is None:
        self.head = new_node
        return
    current = self.head
    while current.next:
        current = current.next
    current.next = new_node

Why O(n)?
You must traverse to the last node.

Optimization: Maintain a tail pointer to make end insertion O(1).

Insert at a Specific Position (O(n))

def insert_at_position(self, index, data):
    new_node = Node(data)

    if index == 0:
        new_node.next = self.head
        self.head = new_node
        return

    current = self.head
    count = 0

    while current and count < index - 1:
        current = current.next
        count += 1

    if current is None:
        raise IndexError("Index out of range")

    new_node.next = current.next
    current.next = new_node

Common mistake: forgetting index == 0 case.


Deletion

Deletion requires careful pointer updates.

Delete by Value (O(n))

def delete_by_value(self, value):
    current = self.head

    if current and current.data == value:
        self.head = current.next
        return

    prev = None
    while current and current.data != value:
        prev = current
        current = current.next

    if current is None:
        return  # not found

    prev.next = current.next

What’s happening:

  • Find node with the value.
  • Link previous node directly to current.next.

Delete by Position (O(n))

def delete_by_position(self, index):
    if self.head is None:
        raise IndexError("Delete from empty list")

    current = self.head

    if index == 0:
        self.head = current.next
        return

    prev = None
    count = 0

    while current and count < index:
        prev = current
        current = current.next
        count += 1

    if current is None:
        raise IndexError("Index out of range")

    prev.next = current.next

Searching (O(n))

def search(self, value):
    current = self.head
    index = 0

    while current:
        if current.data == value:
            return index
        current = current.next
        index += 1

    return -1

Note: Linked lists cannot do binary search efficiently because no random indexing.


Circular Linked Lists and Their Implementation

In a circular linked list, the last node points back to the head, creating a loop.

Structure:

head -> node1 -> node2 -> node3
 ^                       |
 |_______________________|

Why use circular lists?

  • Useful in round-robin scheduling
  • Continuous cycling (like playlists)

Example Implementation (Circular Singly Linked List)

class CircularLinkedList:
    def __init__(self):
        self.head = None

    def append(self, data):
        new_node = Node(data)

        if not self.head:
            self.head = new_node
            new_node.next = self.head
            return

        current = self.head
        while current.next != self.head:
            current = current.next

        current.next = new_node
        new_node.next = self.head

Important: stopping condition is current.next != head (not None).


Applications of Linked Lists

  • Implementing stacks and queues
  • Music playlists / photo galleries
  • Browser history (doubly linked list)
  • Memory allocators (conceptually linked blocks)
  • Graph adjacency lists (often built using linked-like structures)

Summary

Linked lists store data in nodes connected by references. They shine when you need efficient insertions/deletions without shifting elements. Singly linked lists are simpler, doubly linked lists allow backward traversal, and circular linked lists support cyclic processing.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *