Vasu Patel logoVasu Patel
← Python DSA — Course
All chapters in this track
  1. 011. Complexity Analysis
  2. 022. Arrays & Strings
  3. 033. Hashing
  4. 044. Stacks & Queues
  5. 055. Linked Lists
  6. 066. Binary Search
  7. 077. Sorting
  8. 088. Recursion & Backtracking
  9. 099. Trees
  10. 1010. Heaps
  11. 1111. Graphs
  12. 1212. Dynamic Programming
  13. 1313. Tries
  14. 1414. Advanced Structures
  15. 15Appendix: CP Quick Reference

Chapter 5 of 15

5. Linked Lists

Key Insight

Linked list problems almost always use two pointers — either fast/slow (Floyd's cycle) or a trailing pointer for deletion. Because there's no random access, master pointer manipulation patterns rather than algorithmic complexity.

5.1 Node Definition

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

# Build from list (utility)
def build(vals: list[int]) -> ListNode | None:
    dummy = ListNode()
    cur = dummy
    for v in vals:
        cur.next = ListNode(v)
        cur = cur.next
    return dummy.next

# Convert to list (utility)
def to_list(head: ListNode | None) -> list[int]:
    res = []
    while head:
        res.append(head.val)
        head = head.next
    return res

5.2 Core Templates

# --- Reverse a linked list (iterative) --- O(n)
def reverse(head: ListNode | None) -> ListNode | None:
    prev = None
    cur = head
    while cur:
        nxt = cur.next
        cur.next = prev
        prev = cur
        cur = nxt
    return prev

# --- Reverse (recursive) ---
def reverse_rec(head: ListNode | None) -> ListNode | None:
    if not head or not head.next:
        return head
    new_head = reverse_rec(head.next)
    head.next.next = head
    head.next = None
    return new_head

# --- Find middle (slow/fast) ---
def find_middle(head: ListNode) -> ListNode:
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    return slow  # for even length, returns second middle

# --- Detect cycle (Floyd's) ---
def has_cycle(head: ListNode | None) -> bool:
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow is fast:
            return True
    return False

# --- Find cycle start ---
def cycle_start(head: ListNode | None) -> ListNode | None:
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow is fast:
            slow = head
            while slow is not fast:
                slow = slow.next
                fast = fast.next
            return slow
    return None

# --- Merge two sorted lists ---
def merge_sorted(l1: ListNode | None, l2: ListNode | None) -> ListNode | None:
    dummy = ListNode()
    cur = dummy
    while l1 and l2:
        if l1.val <= l2.val:
            cur.next = l1; l1 = l1.next
        else:
            cur.next = l2; l2 = l2.next
        cur = cur.next
    cur.next = l1 or l2
    return dummy.next

# --- Remove Nth from end (one pass) ---
def remove_nth_from_end(head: ListNode, n: int) -> ListNode | None:
    dummy = ListNode(0, head)
    fast = slow = dummy
    for _ in range(n + 1):
        fast = fast.next
    while fast:
        fast = fast.next
        slow = slow.next
    slow.next = slow.next.next
    return dummy.next

# --- Reorder list: L0→L1→…→Ln → L0→Ln→L1→Ln-1→… ---
def reorder_list(head: ListNode | None) -> None:
    if not head:
        return
    mid = find_middle(head)
    second = reverse(mid.next)
    mid.next = None
    first = head
    while second:
        tmp1, tmp2 = first.next, second.next
        first.next = second
        second.next = tmp1
        first, second = tmp1, tmp2

5.3 Key CP Patterns

ProblemTechnique
Cycle detectionFloyd's slow/fast
Kth from endTwo pointers, k gap
Palindrome listReverse second half
Merge k sorted listsHeap + dummy head
Intersection of two listsLength equalization