Linked Lists in Python – DSA

    Table of Contents

    1. Introduction
    2. Singly Linked List
    3. Doubly Linked List
    4. Comparison
    5. Common Interview Problems

    Introduction

    A Linked List is a linear data structure where elements are stored in nodes. Unlike arrays, linked list elements are not stored in contiguous memory locations. Each node contains data and a reference (pointer) to the next node in the sequence.

    Advantages over Arrays:

    • Dynamic size (no need to declare size initially)
    • Easy insertion/deletion at any position
    • No memory wastage

    Disadvantages:

    • No random access (must traverse from head)
    • Extra memory for storing pointers
    • Not cache-friendly

    Singly Linked List

    A singly linked list is a type of linked list where each node contains data and a reference to the next node.

    Structure

    [Data | Next] -> [Data | Next] -> [Data | Next] -> None
    Python

    Implementation

    class Node:
        """Node class for Singly Linked List"""
        def __init__(self, data):
            self.data = data
            self.next = None
    
    
    class SinglyLinkedList:
        """Singly Linked List Implementation"""
    
        def __init__(self):
            self.head = None
            self.size = 0
    
        def is_empty(self):
            """Check if the linked list is empty"""
            return self.head is None
    
        def get_size(self):
            """Return the size of the linked list"""
            return self.size
    
        # ==================== Insertion Operations ====================
    
        def insert_at_beginning(self, data):
            """Insert a node at the beginning - O(1)"""
            new_node = Node(data)
            new_node.next = self.head
            self.head = new_node
            self.size += 1
    
        def insert_at_end(self, data):
            """Insert a node at the end - O(n)"""
            new_node = Node(data)
    
            if self.is_empty():
                self.head = new_node
            else:
                current = self.head
                while current.next:
                    current = current.next
                current.next = new_node
    
            self.size += 1
    
        def insert_at_position(self, data, position):
            """Insert a node at a specific position - O(n)"""
            if position < 0 or position > self.size:
                raise IndexError("Position out of bounds")
    
            if position == 0:
                self.insert_at_beginning(data)
                return
    
            new_node = Node(data)
            current = self.head
    
            for _ in range(position - 1):
                current = current.next
    
            new_node.next = current.next
            current.next = new_node
            self.size += 1
    
        def insert_after_value(self, target_value, data):
            """Insert a node after a specific value - O(n)"""
            if self.is_empty():
                raise ValueError("List is empty")
    
            current = self.head
            while current:
                if current.data == target_value:
                    new_node = Node(data)
                    new_node.next = current.next
                    current.next = new_node
                    self.size += 1
                    return
                current = current.next
    
            raise ValueError(f"Value {target_value} not found in the list")
    
        # ==================== Deletion Operations ====================
    
        def delete_at_beginning(self):
            """Delete the first node - O(1)"""
            if self.is_empty():
                raise IndexError("Cannot delete from empty list")
    
            deleted_data = self.head.data
            self.head = self.head.next
            self.size -= 1
            return deleted_data
    
        def delete_at_end(self):
            """Delete the last node - O(n)"""
            if self.is_empty():
                raise IndexError("Cannot delete from empty list")
    
            if self.head.next is None:
                deleted_data = self.head.data
                self.head = None
                self.size -= 1
                return deleted_data
    
            current = self.head
            while current.next.next:
                current = current.next
    
            deleted_data = current.next.data
            current.next = None
            self.size -= 1
            return deleted_data
    
        def delete_at_position(self, position):
            """Delete a node at a specific position - O(n)"""
            if position < 0 or position >= self.size:
                raise IndexError("Position out of bounds")
    
            if position == 0:
                return self.delete_at_beginning()
    
            current = self.head
            for _ in range(position - 1):
                current = current.next
    
            deleted_data = current.next.data
            current.next = current.next.next
            self.size -= 1
            return deleted_data
    
        def delete_by_value(self, value):
            """Delete the first occurrence of a value - O(n)"""
            if self.is_empty():
                raise ValueError("List is empty")
    
            if self.head.data == value:
                return self.delete_at_beginning()
    
            current = self.head
            while current.next:
                if current.next.data == value:
                    deleted_data = current.next.data
                    current.next = current.next.next
                    self.size -= 1
                    return deleted_data
                current = current.next
    
            raise ValueError(f"Value {value} not found in the list")
    
        # ==================== Search Operations ====================
    
        def search(self, value):
            """Search for a value and return its position - O(n)"""
            current = self.head
            position = 0
    
            while current:
                if current.data == value:
                    return position
                current = current.next
                position += 1
    
            return -1
    
        def get_at_position(self, position):
            """Get the value at a specific position - O(n)"""
            if position < 0 or position >= self.size:
                raise IndexError("Position out of bounds")
    
            current = self.head
            for _ in range(position):
                current = current.next
    
            return current.data
    
        # ==================== Utility Operations ====================
    
        def reverse(self):
            """Reverse the linked list - O(n)"""
            prev = None
            current = self.head
    
            while current:
                next_node = current.next
                current.next = prev
                prev = current
                current = next_node
    
            self.head = prev
    
        def get_middle(self):
            """Get the middle element using slow-fast pointer - O(n)"""
            if self.is_empty():
                return None
    
            slow = fast = self.head
    
            while fast and fast.next:
                slow = slow.next
                fast = fast.next.next
    
            return slow.data
    
        def detect_cycle(self):
            """Detect if there's a cycle using Floyd's algorithm - O(n)"""
            if self.is_empty():
                return False
    
            slow = fast = self.head
    
            while fast and fast.next:
                slow = slow.next
                fast = fast.next.next
    
                if slow == fast:
                    return True
    
            return False
    
        def remove_duplicates(self):
            """Remove duplicates from a sorted list - O(n)"""
            current = self.head
    
            while current and current.next:
                if current.data == current.next.data:
                    current.next = current.next.next
                    self.size -= 1
                else:
                    current = current.next
    
        def display(self):
            """Display the linked list - O(n)"""
            if self.is_empty():
                print("List is empty")
                return
    
            elements = []
            current = self.head
            while current:
                elements.append(str(current.data))
                current = current.next
    
            print(" -> ".join(elements) + " -> None")
    
        def to_list(self):
            """Convert linked list to Python list - O(n)"""
            result = []
            current = self.head
            while current:
                result.append(current.data)
                current = current.next
            return result
    
        def __str__(self):
            """String representation"""
            return " -> ".join(str(x) for x in self.to_list()) + " -> None"
    
        def __len__(self):
            """Return length of the list"""
            return self.size
    
        def __iter__(self):
            """Make the list iterable"""
            current = self.head
            while current:
                yield current.data
                current = current.next
    Python

    Usage Example

    # Create a singly linked list
    sll = SinglyLinkedList()
    
    # Insert elements
    sll.insert_at_end(10)
    sll.insert_at_end(20)
    sll.insert_at_end(30)
    sll.insert_at_beginning(5)
    sll.insert_at_position(15, 2)
    
    print("Original list:")
    sll.display()  # Output: 5 -> 10 -> 15 -> 20 -> 30 -> None
    
    # Delete elements
    sll.delete_by_value(15)
    print("\nAfter deleting 15:")
    sll.display()  # Output: 5 -> 10 -> 20 -> 30 -> None
    
    # Search
    position = sll.search(20)
    print(f"\n20 found at position: {position}")  # Output: 2
    
    # Reverse
    sll.reverse()
    print("\nReversed list:")
    sll.display()  # Output: 30 -> 20 -> 10 -> 5 -> None
    
    # Get middle element
    middle = sll.get_middle()
    print(f"\nMiddle element: {middle}")  # Output: 20
    Python

    Time Complexity Summary

    OperationTime Complexity
    Insert at beginningO(1)
    Insert at endO(n)
    Insert at positionO(n)
    Delete at beginningO(1)
    Delete at endO(n)
    Delete at positionO(n)
    SearchO(n)
    ReverseO(n)
    Access by indexO(n)

    Doubly Linked List

    A doubly linked list is a type of linked list where each node contains data and two references: one to the next node and one to the previous node.

    Structure

    None <- [Prev | Data | Next] <-> [Prev | Data | Next] <-> [Prev | Data | Next] -> None
    Python

    Advantages over Singly Linked List:

    • Can traverse in both directions
    • Deletion is more efficient (no need to track previous node)
    • Can insert before a given node easily

    Disadvantages:

    • Extra memory for storing previous pointer
    • More complex implementation

    Implementation

    class DoublyNode:
        """Node class for Doubly Linked List"""
        def __init__(self, data):
            self.data = data
            self.next = None
            self.prev = None
    
    
    class DoublyLinkedList:
        """Doubly Linked List Implementation"""
    
        def __init__(self):
            self.head = None
            self.tail = None
            self.size = 0
    
        def is_empty(self):
            """Check if the linked list is empty"""
            return self.head is None
    
        def get_size(self):
            """Return the size of the linked list"""
            return self.size
    
        # ==================== Insertion Operations ====================
    
        def insert_at_beginning(self, data):
            """Insert a node at the beginning - O(1)"""
            new_node = DoublyNode(data)
    
            if self.is_empty():
                self.head = self.tail = new_node
            else:
                new_node.next = self.head
                self.head.prev = new_node
                self.head = new_node
    
            self.size += 1
    
        def insert_at_end(self, data):
            """Insert a node at the end - O(1)"""
            new_node = DoublyNode(data)
    
            if self.is_empty():
                self.head = self.tail = new_node
            else:
                new_node.prev = self.tail
                self.tail.next = new_node
                self.tail = new_node
    
            self.size += 1
    
        def insert_at_position(self, data, position):
            """Insert a node at a specific position - O(n)"""
            if position < 0 or position > self.size:
                raise IndexError("Position out of bounds")
    
            if position == 0:
                self.insert_at_beginning(data)
                return
    
            if position == self.size:
                self.insert_at_end(data)
                return
    
            new_node = DoublyNode(data)
            current = self.head
    
            for _ in range(position):
                current = current.next
    
            new_node.prev = current.prev
            new_node.next = current
            current.prev.next = new_node
            current.prev = new_node
    
            self.size += 1
    
        def insert_after_value(self, target_value, data):
            """Insert a node after a specific value - O(n)"""
            if self.is_empty():
                raise ValueError("List is empty")
    
            current = self.head
            while current:
                if current.data == target_value:
                    new_node = DoublyNode(data)
                    new_node.prev = current
                    new_node.next = current.next
    
                    if current.next:
                        current.next.prev = new_node
                    else:
                        self.tail = new_node
    
                    current.next = new_node
                    self.size += 1
                    return
                current = current.next
    
            raise ValueError(f"Value {target_value} not found in the list")
    
        def insert_before_value(self, target_value, data):
            """Insert a node before a specific value - O(n)"""
            if self.is_empty():
                raise ValueError("List is empty")
    
            current = self.head
            while current:
                if current.data == target_value:
                    new_node = DoublyNode(data)
                    new_node.next = current
                    new_node.prev = current.prev
    
                    if current.prev:
                        current.prev.next = new_node
                    else:
                        self.head = new_node
    
                    current.prev = new_node
                    self.size += 1
                    return
                current = current.next
    
            raise ValueError(f"Value {target_value} not found in the list")
    
        # ==================== Deletion Operations ====================
    
        def delete_at_beginning(self):
            """Delete the first node - O(1)"""
            if self.is_empty():
                raise IndexError("Cannot delete from empty list")
    
            deleted_data = self.head.data
    
            if self.head == self.tail:
                self.head = self.tail = None
            else:
                self.head = self.head.next
                self.head.prev = None
    
            self.size -= 1
            return deleted_data
    
        def delete_at_end(self):
            """Delete the last node - O(1)"""
            if self.is_empty():
                raise IndexError("Cannot delete from empty list")
    
            deleted_data = self.tail.data
    
            if self.head == self.tail:
                self.head = self.tail = None
            else:
                self.tail = self.tail.prev
                self.tail.next = None
    
            self.size -= 1
            return deleted_data
    
        def delete_at_position(self, position):
            """Delete a node at a specific position - O(n)"""
            if position < 0 or position >= self.size:
                raise IndexError("Position out of bounds")
    
            if position == 0:
                return self.delete_at_beginning()
    
            if position == self.size - 1:
                return self.delete_at_end()
    
            current = self.head
            for _ in range(position):
                current = current.next
    
            deleted_data = current.data
            current.prev.next = current.next
            current.next.prev = current.prev
    
            self.size -= 1
            return deleted_data
    
        def delete_by_value(self, value):
            """Delete the first occurrence of a value - O(n)"""
            if self.is_empty():
                raise ValueError("List is empty")
    
            current = self.head
    
            while current:
                if current.data == value:
                    if current == self.head:
                        return self.delete_at_beginning()
                    elif current == self.tail:
                        return self.delete_at_end()
                    else:
                        current.prev.next = current.next
                        current.next.prev = current.prev
                        self.size -= 1
                        return current.data
                current = current.next
    
            raise ValueError(f"Value {value} not found in the list")
    
        # ==================== Search Operations ====================
    
        def search(self, value):
            """Search for a value and return its position - O(n)"""
            current = self.head
            position = 0
    
            while current:
                if current.data == value:
                    return position
                current = current.next
                position += 1
    
            return -1
    
        def get_at_position(self, position):
            """Get the value at a specific position - O(n)"""
            if position < 0 or position >= self.size:
                raise IndexError("Position out of bounds")
    
            # Optimize: start from tail if position is in second half
            if position < self.size // 2:
                current = self.head
                for _ in range(position):
                    current = current.next
            else:
                current = self.tail
                for _ in range(self.size - position - 1):
                    current = current.prev
    
            return current.data
    
        # ==================== Utility Operations ====================
    
        def reverse(self):
            """Reverse the doubly linked list - O(n)"""
            if self.is_empty():
                return
    
            current = self.head
            self.head, self.tail = self.tail, self.head
    
            while current:
                current.prev, current.next = current.next, current.prev
                current = current.prev
    
        def get_middle(self):
            """Get the middle element using slow-fast pointer - O(n)"""
            if self.is_empty():
                return None
    
            slow = fast = self.head
    
            while fast and fast.next:
                slow = slow.next
                fast = fast.next.next
    
            return slow.data
    
        def remove_duplicates(self):
            """Remove duplicates from a sorted list - O(n)"""
            current = self.head
    
            while current and current.next:
                if current.data == current.next.data:
                    node_to_delete = current.next
                    current.next = node_to_delete.next
    
                    if node_to_delete.next:
                        node_to_delete.next.prev = current
                    else:
                        self.tail = current
    
                    self.size -= 1
                else:
                    current = current.next
    
        def display_forward(self):
            """Display the linked list forward - O(n)"""
            if self.is_empty():
                print("List is empty")
                return
    
            elements = []
            current = self.head
            while current:
                elements.append(str(current.data))
                current = current.next
    
            print("None <- " + " <-> ".join(elements) + " -> None")
    
        def display_backward(self):
            """Display the linked list backward - O(n)"""
            if self.is_empty():
                print("List is empty")
                return
    
            elements = []
            current = self.tail
            while current:
                elements.append(str(current.data))
                current = current.prev
    
            print("None <- " + " <-> ".join(elements) + " -> None")
    
        def to_list(self):
            """Convert linked list to Python list - O(n)"""
            result = []
            current = self.head
            while current:
                result.append(current.data)
                current = current.next
            return result
    
        def __str__(self):
            """String representation"""
            return " <-> ".join(str(x) for x in self.to_list())
    
        def __len__(self):
            """Return length of the list"""
            return self.size
    
        def __iter__(self):
            """Make the list iterable (forward)"""
            current = self.head
            while current:
                yield current.data
                current = current.next
    
        def reverse_iter(self):
            """Iterate backward"""
            current = self.tail
            while current:
                yield current.data
                current = current.prev
    Python

    Usage Example

    # Create a doubly linked list
    dll = DoublyLinkedList()
    
    # Insert elements
    dll.insert_at_end(10)
    dll.insert_at_end(20)
    dll.insert_at_end(30)
    dll.insert_at_beginning(5)
    dll.insert_at_position(15, 2)
    
    print("Original list (forward):")
    dll.display_forward()  # Output: None <- 5 <-> 10 <-> 15 <-> 20 <-> 30 -> None
    
    print("\nOriginal list (backward):")
    dll.display_backward()  # Output: None <- 30 <-> 20 <-> 15 <-> 10 <-> 5 -> None
    
    # Delete elements
    dll.delete_by_value(15)
    print("\nAfter deleting 15:")
    dll.display_forward()  # Output: None <- 5 <-> 10 <-> 20 <-> 30 -> None
    
    # Insert before value
    dll.insert_before_value(20, 15)
    print("\nAfter inserting 15 before 20:")
    dll.display_forward()  # Output: None <- 5 <-> 10 <-> 15 <-> 20 <-> 30 -> None
    
    # Reverse
    dll.reverse()
    print("\nReversed list:")
    dll.display_forward()  # Output: None <- 30 <-> 20 <-> 15 <-> 10 <-> 5 -> None
    
    # Iterate backward
    print("\nBackward iteration:")
    for value in dll.reverse_iter():
        print(value, end=" ")  # Output: 5 10 15 20 30
    Python

    Time Complexity Summary

    OperationSingly Linked ListDoubly Linked List
    Insert at beginningO(1)O(1)
    Insert at endO(n)O(1) ⭐
    Insert at positionO(n)O(n)
    Delete at beginningO(1)O(1)
    Delete at endO(n)O(1) ⭐
    Delete at positionO(n)O(n)
    SearchO(n)O(n)
    ReverseO(n)O(n)

    Comparison

    Singly Linked List vs Doubly Linked List

    FeatureSingly Linked ListDoubly Linked List
    Pointers per node1 (next)2 (next, prev)
    Memory usageLessMore
    TraversalForward onlyBoth directions
    Insertion at endO(n) without tailO(1) with tail
    Deletion at endO(n)O(1) with tail
    ComplexitySimplerMore complex
    Use casesStack, simple listsLRU Cache, Browser history

    When to Use Which?

    Use Singly Linked List when:

    • Memory is limited
    • You only need forward traversal
    • Implementing a stack
    • Simple data structure requirements

    Use Doubly Linked List when:

    • You need bidirectional traversal
    • Frequent deletions at both ends
    • Implementing LRU cache, deque, browser history
    • Navigation is important (undo/redo operations)

    Common Interview Problems

    1. Reverse a Linked List

    def reverse_linked_list(head):
        """Reverse a singly linked list - O(n) time, O(1) space"""
        prev = None
        current = head
    
        while current:
            next_node = current.next
            current.next = prev
            prev = current
            current = next_node
    
        return prev
    Python

    2. Detect Cycle in Linked List (Floyd’s Cycle Detection)

    def has_cycle(head):
        """Detect cycle using slow-fast pointer - O(n) time, O(1) space"""
        if not head or not head.next:
            return False
    
        slow = fast = head
    
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
    
            if slow == fast:
                return True
    
        return False
    Python

    3. Find Middle of Linked List

    def find_middle(head):
        """Find middle using slow-fast pointer - O(n) time, O(1) space"""
        if not head:
            return None
    
        slow = fast = head
    
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
    
        return slow
    Python

    4. Merge Two Sorted Linked Lists

    def merge_sorted_lists(l1, l2):
        """Merge two sorted linked lists - O(n+m) time, O(1) space"""
        dummy = Node(0)
        current = dummy
    
        while l1 and l2:
            if l1.data <= l2.data:
                current.next = l1
                l1 = l1.next
            else:
                current.next = l2
                l2 = l2.next
            current = current.next
    
        current.next = l1 if l1 else l2
    
        return dummy.next
    Python

    5. Remove Nth Node From End

    def remove_nth_from_end(head, n):
        """Remove nth node from end - O(n) time, O(1) space"""
        dummy = Node(0)
        dummy.next = head
    
        first = second = dummy
    
        # Move first n+1 steps ahead
        for _ in range(n + 1):
            if first is None:
                return head
            first = first.next
    
        # Move both pointers
        while first:
            first = first.next
            second = second.next
    
        # Remove the node
        second.next = second.next.next
    
        return dummy.next
    Python

    6. Check if Linked List is Palindrome

    def is_palindrome(head):
        """Check if linked list is palindrome - O(n) time, O(1) space"""
        if not head or not head.next:
            return True
    
        # Find middle
        slow = fast = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
    
        # Reverse second half
        prev = None
        current = slow
        while current:
            next_node = current.next
            current.next = prev
            prev = current
            current = next_node
    
        # Compare both halves
        left, right = head, prev
        while right:
            if left.data != right.data:
                return False
            left = left.next
            right = right.next
    
        return True
    Python

    7. Intersection of Two Linked Lists

    def get_intersection_node(headA, headB):
        """Find intersection point - O(m+n) time, O(1) space"""
        if not headA or not headB:
            return None
    
        ptrA, ptrB = headA, headB
    
        # When one pointer reaches end, redirect to other head
        while ptrA != ptrB:
            ptrA = ptrA.next if ptrA else headB
            ptrB = ptrB.next if ptrB else headA
    
        return ptrA  # Either intersection node or None
    Python

    8. Remove Duplicates from Sorted List

    def remove_duplicates(head):
        """Remove duplicates from sorted list - O(n) time, O(1) space"""
        current = head
    
        while current and current.next:
            if current.data == current.next.data:
                current.next = current.next.next
            else:
                current = current.next
    
        return head
    Python

    9. Add Two Numbers Represented as Linked Lists

    def add_two_numbers(l1, l2):
        """Add two numbers represented as linked lists - O(max(m,n)) time"""
        dummy = Node(0)
        current = dummy
        carry = 0
    
        while l1 or l2 or carry:
            val1 = l1.data if l1 else 0
            val2 = l2.data if l2 else 0
    
            total = val1 + val2 + carry
            carry = total // 10
    
            current.next = Node(total % 10)
            current = current.next
    
            if l1: l1 = l1.next
            if l2: l2 = l2.next
    
        return dummy.next
    Python

    10. Rotate Linked List

    def rotate_right(head, k):
        """Rotate linked list to the right by k places - O(n) time"""
        if not head or not head.next or k == 0:
            return head
    
        # Find length and make it circular
        length = 1
        current = head
        while current.next:
            current = current.next
            length += 1
    
        current.next = head  # Make circular
    
        # Find new tail: (length - k % length - 1)th node
        k = k % length
        steps_to_new_tail = length - k - 1
    
        new_tail = head
        for _ in range(steps_to_new_tail):
            new_tail = new_tail.next
    
        new_head = new_tail.next
        new_tail.next = None
    
        return new_head
    Python

    11. Clone a Linked List with Random Pointer

    class RandomNode:
        def __init__(self, data):
            self.data = data
            self.next = None
            self.random = None
    
    def copy_random_list(head):
        """Clone a linked list with random pointer - O(n) time, O(n) space"""
        if not head:
            return None
    
        # Create mapping from old nodes to new nodes
        old_to_new = {}
    
        # First pass: create all nodes
        current = head
        while current:
            old_to_new[current] = RandomNode(current.data)
            current = current.next
    
        # Second pass: assign next and random pointers
        current = head
        while current:
            if current.next:
                old_to_new[current].next = old_to_new[current.next]
            if current.random:
                old_to_new[current].random = old_to_new[current.random]
            current = current.next
    
        return old_to_new[head]
    Python

    12. Flatten a Multilevel Doubly Linked List

    def flatten(head):
        """Flatten a multilevel doubly linked list - O(n) time, O(n) space"""
        if not head:
            return None
    
        stack = []
        current = head
    
        while current:
            if current.child:
                if current.next:
                    stack.append(current.next)
    
                current.next = current.child
                current.child.prev = current
                current.child = None
    
            if not current.next and stack:
                next_node = stack.pop()
                current.next = next_node
                next_node.prev = current
    
            current = current.next
    
        return head
    Python

    Additional Tips

    Memory Management

    • In Python, garbage collection handles memory automatically
    • Break circular references before deleting (set references to None)
    • Use del to explicitly remove references if needed

    Best Practices

    1. Always check for empty list before operations
    2. Handle edge cases (single node, two nodes)
    3. Update size counter consistently
    4. Maintain both head and tail pointers for doubly linked lists
    5. Use dummy/sentinel nodes for simplification in certain algorithms

    Common Mistakes to Avoid

    • Losing reference to head while traversing
    • Not updating tail pointer in doubly linked list
    • Forgetting to update previous pointers in doubly linked list
    • Off-by-one errors in position-based operations
    • Not handling None values properly

    Practice Problems

    1. LeetCode Easy:
      • Reverse Linked List (206)
      • Merge Two Sorted Lists (21)
      • Remove Duplicates from Sorted List (83)
      • Linked List Cycle (141)
      • Palindrome Linked List (234)
    2. LeetCode Medium:
      • Add Two Numbers (2)
      • Remove Nth Node From End (19)
      • Rotate List (61)
      • Swap Nodes in Pairs (24)
      • Reorder List (143)
    3. LeetCode Hard:
      • Merge k Sorted Lists (23)
      • Reverse Nodes in k-Group (25)
      • Copy List with Random Pointer (138)

    Conclusion

    Linked lists are fundamental data structures that provide flexibility in memory allocation and efficient insertion/deletion operations. While they have drawbacks like no random access and extra memory for pointers, they excel in scenarios requiring dynamic size and frequent modifications.

    Key Takeaways:

    • Singly linked lists are simpler and use less memory
    • Doubly linked lists allow bidirectional traversal and more efficient operations
    • Master the two-pointer technique for many linked list problems
    • Practice edge cases: empty list, single node, two nodes
    • Understand when to use linked lists over arrays

    Happy Coding! 🚀


    Discover more from Altgr Blog

    Subscribe to get the latest posts sent to your email.

    Leave a Reply

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