Singly Linked List

Insert, delete, and traverse nodes - see pointers link elements one direction.

Time O(n) · Space O(n)

Singly Linked List Visualization

Insert
Delete
Access
Utils

Implementation

Python
class SllNode:
    """A single node in the singly linked list."""

    def __init__(self, val):
        self.val = val
        self.next = None


class SllList:
    """
    Singly Linked List implementation.

    Provides operations for adding, removing, searching, and
    traversing elements in a one-directional linked structure.
    """

    def __init__(self, val=None):
        """Initialize the list, optionally with a starting value."""
        if val is not None:
            self.head = SllNode(val)
            self.len = 1
        else:
            self.head = None
            self.len = 0

    def __len__(self):
        """Return the number of nodes in the list."""
        return self.len

    def __str__(self):
        """String representation: head -> ... -> tail."""
        values = [str(el) for el in self]
        return " -> ".join(values)

    def __repr__(self):
        return str(self)

    def __iter__(self):
        """Iterate over all values in the list."""
        cur = self.head
        while cur:
            yield cur.val
            cur = cur.next

    def __bool__(self):
        """Return True if the list is not empty."""
        return self.len > 0

    def get_at(self, position) -> SllNode:
        """
        Get the node at the specified position.
        Raises IndexError if position is invalid.
        """
        if position >= self.len or position < 0:
            raise IndexError("Position is invalid.")
        node = self.head
        for _ in range(position):
            node = node.next
        return node

    def add_left(self, val):
        """Add a new node with the given value at the head."""
        new_head = SllNode(val)
        new_head.next = self.head
        self.head = new_head
        self.len += 1

    def add_right(self, val):
        """Add a new node with the given value at the tail."""
        if not self:
            self.head = SllNode(val)
        else:
            tail = self.get_at(self.len - 1)
            tail.next = SllNode(val)
        self.len += 1

    def add_at(self, val, position):
        """
        Insert a new node at the specified position.
        Raises IndexError if position is out of bounds.
        """
        if position > len(self) or position < 0:
            raise IndexError("Invalid position to insert a value at.")
        if position == 0:
            return self.add_left(val)
        if position == len(self):
            return self.add_right(val)
        before = self.get_at(position - 1)
        after = before.next
        before.next = SllNode(val)
        before.next.next = after
        self.len += 1

    def remove_left(self):
        """
        Remove and discard the head node.
        Raises IndexError if list is empty.
        """
        if not self:
            raise IndexError("List is empty.")
        self.head = self.head.next
        self.len -= 1

    def remove_right(self):
        """
        Remove and discard the tail node.
        Raises IndexError if list is empty.
        """
        if not self:
            raise IndexError("List is empty.")
        if self.len == 1:
            self.head = None
        else:
            before_tail = self.get_at(self.len - 2)
            before_tail.next = None
        self.len -= 1

    def remove_at(self, position):
        """
        Remove the node at the specified position.
        Raises IndexError if list is empty or position is invalid.
        """
        if not self:
            raise IndexError("List is empty.")
        if position < 0 or position >= self.len:
            raise IndexError("Position is invalid.")
        if position == 0:
            return self.remove_left()
        before = self.get_at(position - 1)
        to_remove = before.next
        before.next = before.next.next
        to_remove.next = None
        self.len -= 1

    def traverse(self):
        """Return a list of all values in the list."""
        cur = self.head
        arr = []
        while cur:
            arr.append(cur.val)
            cur = cur.next
        return arr

    def search_for(self, val):
        """
        Search for a value in the list.
        Returns the position if found, -1 otherwise.
        """
        cur = self.head
        pos = 0
        while cur:
            if cur.val == val:
                return pos
            pos += 1
            cur = cur.next
        return -1

    def reverse_list(self):
        """Reverse the linked list in place."""
        prev = None
        cur = self.head
        while cur:
            # This one-liner only works in Python! - tuple unpacking evaluates all vars on right first
            # cur.next, prev, cur = prev, cur, cur.next

            # Standard approach - store 3 variables
            next = cur.next
            cur.next = prev
            prev = cur
            cur = next

        self.head = prev


# Example usage:
if __name__ == "__main__":
    # Create a new list
    sll = SllList()

    # Add elements
    sll.add_right(10)
    sll.add_right(20)
    sll.add_right(30)
    print(sll)  # 10 -> 20 -> 30

    # Add to head
    sll.add_left(5)
    print(sll)  # 5 -> 10 -> 20 -> 30

    # Add at position
    sll.add_at(15, 2)
    print(sll)  # 5 -> 10 -> 15 -> 20 -> 30

    # Search for a value
    pos = sll.search_for(20)
    print(f"Found 20 at position: {pos}")  # Found 20 at position: 3

    # Get value at position
    node = sll.get_at(2)
    print(f"Value at position 2: {node.val}")  # Value at position 2: 15

    # Remove elements
    sll.remove_left()
    print(sll)  # 10 -> 15 -> 20 -> 30

    sll.remove_right()
    print(sll)  # 10 -> 15 -> 20

    # Reverse the list
    sll.reverse_list()
    print(sll)  # 20 -> 15 -> 10
About Singly Linked List

How Singly Linked List Works

A singly linked list is a linear data structure where each node stores data and a pointer to the next node. Unlike arrays, linked lists allow efficient insertion and deletion without shifting elements, but do not support constant-time random access.

Each node contains a value and a reference to the next node. Insertion at the head is O(1). To insert or delete at a specific position, traverse from the head until you reach the target node, then update the pointers.

Complexity

Time
O(n)
Space
O(n)

When to Use Singly Linked List

  • Dynamic memory allocation where size is unknown
  • Implementing stacks and queues
  • Situations where frequent insertions and deletions are needed