title: Reorder List
slug: reorder-list
difficulty: medium
leetcode_id: 143
leetcode_url: https://leetcode.com/problems/reorder-list/
categories:
  - linked-lists
  - two-pointers
patterns:
  - slug: fast-slow-pointers
    is_optimal: true
  - slug: linkedlist-reversal
    is_optimal: false

function_signature: "def reorder_list(head: ListNode) -> None:"

test_cases:
  visible:
    - input: { head: [1, 2, 3, 4] }
      expected: [1, 4, 2, 3]
    - input: { head: [1, 2, 3, 4, 5] }
      expected: [1, 5, 2, 4, 3]
  hidden:
    - input: { head: [1] }
      expected: [1]
    - input: { head: [1, 2] }
      expected: [1, 2]
    - input: { head: [1, 2, 3] }
      expected: [1, 3, 2]
    - input: { head: [1, 2, 3, 4, 5, 6] }
      expected: [1, 6, 2, 5, 3, 4]
    - input: { head: [1, 2, 3, 4, 5, 6, 7] }
      expected: [1, 7, 2, 6, 3, 5, 4]

description: |
  You are given the head of a singly linked-list. The list can be represented as:

  ```
  L0 → L1 → … → Ln-1 → Ln
  ```

  *Reorder the list to be on the following form:*

  ```
  L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …
  ```

  You may not modify the values in the list's nodes. Only nodes themselves may be changed.

constraints: |
  - The number of nodes in the list is in the range `[1, 5 * 10^4]`
  - `1 <= Node.val <= 1000`

examples:
  - input: "head = [1,2,3,4]"
    output: "[1,4,2,3]"
    explanation: "The original list is 1→2→3→4. After reordering: take the first node (1), then the last node (4), then the second node (2), then the second-to-last node (3). Result: 1→4→2→3."
  - input: "head = [1,2,3,4,5]"
    output: "[1,5,2,4,3]"
    explanation: "The original list is 1→2→3→4→5. After reordering: 1 (first), 5 (last), 2 (second), 4 (second-to-last), 3 (middle). Result: 1→5→2→4→3."

explanation:
  intuition: |
    Imagine you have a deck of cards numbered 1 through n, laid out in a row. The reordering pattern asks you to interleave from both ends: pick the first card, then the last card, then the second card, then the second-to-last card, and so on until you meet in the middle.

    The challenge with a singly linked list is that you can only traverse forward — you can't easily access the last node or go backwards. So how do we efficiently get to the nodes from the end?

    The key insight is to **split the list in half, reverse the second half, then merge the two halves**. After reversing the second half, both halves start from the "outside" positions we need:
    - First half: `L0 → L1 → L2 → ...`
    - Reversed second half: `Ln → Ln-1 → Ln-2 → ...`

    Now we can simply alternate between them, picking one node from each half in turn.

  approach: |
    We solve this using a **three-step approach** combining classic linked list techniques:

    **Step 1: Find the middle of the list**

    - Use the **fast-slow pointer** technique (also called the tortoise and hare)
    - `slow` moves one step at a time, `fast` moves two steps
    - When `fast` reaches the end, `slow` is at the middle
    - This gives us the split point between the two halves

    &nbsp;

    **Step 2: Reverse the second half**

    - Starting from the node after `slow`, reverse the linked list in-place
    - Use three pointers: `prev`, `curr`, and `next`
    - After reversal, we have the second half pointing backwards from the last node

    &nbsp;

    **Step 3: Merge the two halves**

    - Alternate nodes from the first half and the reversed second half
    - `first` pointer walks through the first half
    - `second` pointer walks through the reversed second half
    - Interleave by adjusting `next` pointers: first→second→first→second→...

    &nbsp;

    **Step 4: Return**

    - The modification is done in-place, so no return value is needed
    - The original `head` now points to the reordered list

  common_pitfalls:
    - title: Using O(n) Extra Space
      description: |
        A common first approach is to copy all nodes into an array, then rebuild the list by picking from both ends of the array. While this works, it uses **O(n) extra space**.

        The optimal solution reverses the second half in-place, achieving **O(1) space** (only a few pointers).
      wrong_approach: "Store nodes in array and rebuild"
      correct_approach: "Reverse second half in-place, then merge"

    - title: Off-by-One Errors in Finding Middle
      description: |
        The middle-finding logic needs care, especially for even vs odd length lists:
        - For `[1,2,3,4]`, we want `slow` at node 2, so second half is `[3,4]`
        - For `[1,2,3,4,5]`, we want `slow` at node 3, so second half is `[4,5]`

        Using `while fast and fast.next` with `slow` starting at `head` gives the correct split. Be careful not to lose the connection when splitting.
      wrong_approach: "Inconsistent middle calculation"
      correct_approach: "Use standard fast-slow with while fast.next and fast.next.next"

    - title: Forgetting to Terminate the First Half
      description: |
        After finding the middle, you must set `slow.next = None` to terminate the first half. Otherwise, you'll have cycles or incorrect lengths when merging.

        For example, with `[1,2,3,4]`, after splitting, first half should be `1→2→None` and second half `3→4→None`. Without terminating, first half would still point to 3.
      wrong_approach: "Not breaking the link at the middle"
      correct_approach: "Set slow.next = None before reversing"

    - title: Incorrect Merge Order
      description: |
        When merging, remember you're alternating: first half node, then second half node. Save the next pointers before rewiring:

        ```python
        tmp1, tmp2 = first.next, second.next
        first.next = second
        second.next = tmp1
        first, second = tmp1, tmp2
        ```

        Failing to save `next` pointers before modifying them leads to lost nodes.
      wrong_approach: "Modifying next pointers without saving"
      correct_approach: "Save both next pointers before any rewiring"

  key_takeaways:
    - "**Fast-slow pointer pattern**: Essential technique for finding the middle of a linked list in O(n) time and O(1) space"
    - "**In-place reversal**: Reversing a linked list with three pointers (`prev`, `curr`, `next`) is a fundamental operation"
    - "**Decompose complex operations**: Breaking the problem into find-middle, reverse, and merge makes it manageable"
    - "**Related problems**: This pattern applies to Palindrome Linked List, Sort List, and other problems requiring middle-finding or reversal"

  time_complexity: "O(n). We traverse the list three times: once to find the middle, once to reverse the second half, and once to merge. Each pass is O(n), so overall O(n)."
  space_complexity: "O(1). We only use a constant number of pointer variables (`slow`, `fast`, `prev`, `curr`, `first`, `second`, etc.), regardless of list size."

solutions:
  - approach_name: Three-Step In-Place
    is_optimal: true
    code: |
      class ListNode:
          def __init__(self, val=0, next=None):
              self.val = val
              self.next = next

      def reorder_list(head: ListNode) -> None:
          if not head or not head.next:
              return

          # Step 1: Find the middle using fast-slow pointers
          slow, fast = head, head
          while fast.next and fast.next.next:
              slow = slow.next
              fast = fast.next.next

          # Step 2: Reverse the second half
          # slow is at the middle, second half starts at slow.next
          prev, curr = None, slow.next
          slow.next = None  # Terminate the first half

          while curr:
              next_temp = curr.next  # Save next node
              curr.next = prev       # Reverse the pointer
              prev = curr            # Move prev forward
              curr = next_temp       # Move curr forward

          # prev now points to the head of reversed second half

          # Step 3: Merge the two halves
          first, second = head, prev
          while second:
              # Save next pointers
              tmp1, tmp2 = first.next, second.next
              # Interleave
              first.next = second
              second.next = tmp1
              # Move to next pair
              first, second = tmp1, tmp2
    explanation: |
      **Time Complexity:** O(n) — Three linear passes through the list.

      **Space Complexity:** O(1) — Only a constant number of pointers used.

      This solution combines three classic linked list operations: finding the middle with fast-slow pointers, reversing a linked list in-place, and merging two lists. Each operation is O(n) time and O(1) space.

  - approach_name: Stack-Based
    is_optimal: false
    code: |
      class ListNode:
          def __init__(self, val=0, next=None):
              self.val = val
              self.next = next

      def reorder_list(head: ListNode) -> None:
          if not head or not head.next:
              return

          # Store all nodes in a list for random access
          nodes = []
          curr = head
          while curr:
              nodes.append(curr)
              curr = curr.next

          # Use two pointers on the array
          left, right = 0, len(nodes) - 1

          while left < right:
              # Connect left to right
              nodes[left].next = nodes[right]
              left += 1

              if left == right:
                  break

              # Connect right to next left
              nodes[right].next = nodes[left]
              right -= 1

          # Terminate the list
          nodes[left].next = None
    explanation: |
      **Time Complexity:** O(n) — One pass to collect nodes, one pass to rewire.

      **Space Complexity:** O(n) — Array storing all node references.

      This approach trades space for simplicity. By storing nodes in an array, we gain random access and can easily pick from both ends. While correct, it uses O(n) extra space, making it suboptimal compared to the in-place solution.
