title: Merge Two Sorted Lists
slug: merge-two-sorted-lists
difficulty: easy
leetcode_id: 21
leetcode_url: https://leetcode.com/problems/merge-two-sorted-lists/
categories:
  - linked-lists
  - recursion
patterns:
  - two-pointers

function_signature: "def merge_two_lists(list1: ListNode | None, list2: ListNode | None) -> ListNode | None:"

test_cases:
  visible:
    - input: { list1: [1, 2, 4], list2: [1, 3, 4] }
      expected: [1, 1, 2, 3, 4, 4]
    - input: { list1: [], list2: [] }
      expected: []
    - input: { list1: [], list2: [0] }
      expected: [0]
  hidden:
    - input: { list1: [1], list2: [2] }
      expected: [1, 2]
    - input: { list1: [5], list2: [1, 2, 4] }
      expected: [1, 2, 4, 5]

description: |
  You are given the heads of two sorted linked lists `list1` and `list2`.

  Merge the two lists into one **sorted** list. The list should be made by splicing together the nodes of the first two lists.

  Return *the head of the merged linked list*.

constraints: |
  - The number of nodes in both lists is in the range `[0, 50]`
  - `-100 <= Node.val <= 100`
  - Both `list1` and `list2` are sorted in **non-decreasing** order

examples:
  - input: "list1 = [1,2,4], list2 = [1,3,4]"
    output: "[1,1,2,3,4,4]"
    explanation: "Merge by comparing heads and taking the smaller value each time."
  - input: "list1 = [], list2 = []"
    output: "[]"
    explanation: "Both lists empty, result is empty."
  - input: "list1 = [], list2 = [0]"
    output: "[0]"
    explanation: "One list empty, return the other."

explanation:
  intuition: |
    Imagine you have two sorted piles of numbered cards, and you want to combine them into one sorted pile. The natural approach is to always take the smaller of the two top cards and add it to your result pile.

    Think of it like the **merge step in merge sort** — you have two already-sorted sequences, and you need to combine them while maintaining sorted order. The key insight is that since both lists are sorted, the smallest unprocessed element is always at the front of one of the two lists.

    The **dummy node technique** is a powerful pattern for linked list construction. Instead of handling the "first node" as a special case, we create a placeholder node to start our result list. This lets us always use `current.next = ...` without checking if we're setting the head.

  approach: |
    We solve this using an **Iterative Merge with Dummy Node**:

    **Step 1: Create a dummy node and current pointer**

    - Create a `dummy` node as a placeholder (its value doesn't matter)
    - Set `current` to point to `dummy` — this is where we'll build our result
    - The dummy eliminates special-case logic for initialising the head

    &nbsp;

    **Step 2: Compare and attach nodes**

    - While both lists have remaining nodes:
      - Compare the values at the heads of `list1` and `list2`
      - Attach the smaller node to `current.next`
      - Advance the pointer of whichever list we took from
      - Move `current` forward to the newly attached node

    &nbsp;

    **Step 3: Attach remaining nodes**

    - When the loop ends, one list may still have nodes
    - Simply attach the entire remaining portion: `current.next = list1 or list2`
    - No need to iterate — the remaining nodes are already sorted and linked!

    &nbsp;

    **Step 4: Return the merged list**

    - Return `dummy.next` — this skips the placeholder and gives the actual head
    - The dummy node itself is not part of the result

  common_pitfalls:
    - title: Forgetting to Handle Empty Lists
      description: |
        One or both input lists might be `None`. Without the dummy node pattern, you'd need explicit null checks to initialise the result head.

        For example, if `list1` is empty, the correct answer is simply `list2`. The dummy node handles this naturally — if one list is empty, the loop never runs, and we just attach the non-empty list.
      wrong_approach: "Assuming both lists have at least one node"
      correct_approach: "Use dummy node, which handles empty lists automatically"

    - title: Iterating Through Remaining Nodes One-by-One
      description: |
        After the main loop, one list might still have nodes. A common mistake is to loop through them individually.

        Since the remaining nodes are already sorted and linked together, you can attach the entire remainder with a single assignment: `current.next = list1 if list1 else list2`. This is O(1), not O(remaining length).
      wrong_approach: "while list1: current.next = list1; list1 = list1.next"
      correct_approach: "current.next = list1 or list2"

    - title: Returning dummy Instead of dummy.next
      description: |
        The dummy node is just a construction helper — it's not part of the actual result. The merged list starts at `dummy.next`.

        Returning `dummy` would include an extra node with whatever value you initialised it with (usually 0).
      wrong_approach: "return dummy"
      correct_approach: "return dummy.next"

  key_takeaways:
    - "**Dummy node pattern**: Eliminates special-case handling for the head node in linked list construction"
    - "**This IS merge sort's merge step**: Understanding this prepares you for implementing full merge sort on linked lists"
    - "**Compare and advance**: The two-pointer technique of comparing heads and advancing one pointer is fundamental to many linked list problems"
    - "**Recursive alternative**: This problem has an elegant recursive solution that's worth understanding, though it uses O(n+m) stack space"

  time_complexity: "O(n + m). We visit each node exactly once, where n and m are the lengths of the two lists."
  space_complexity: "O(1). We only use a few pointer variables; we reuse existing nodes without allocating new ones."

solutions:
  - approach_name: Iterative with Dummy Node
    is_optimal: true
    code: |
      class ListNode:
          def __init__(self, val=0, next=None):
              self.val = val
              self.next = next

      def merge_two_lists(
          list1: ListNode | None,
          list2: ListNode | None,
      ) -> ListNode | None:
          # Dummy node simplifies head handling
          dummy = ListNode()
          current = dummy

          # Compare and attach smaller node each iteration
          while list1 and list2:
              if list1.val <= list2.val:
                  current.next = list1
                  list1 = list1.next
              else:
                  current.next = list2
                  list2 = list2.next
              current = current.next

          # Attach any remaining nodes (already sorted)
          current.next = list1 if list1 else list2

          # Skip dummy, return actual head
          return dummy.next
    explanation: |
      **Time Complexity:** O(n + m) — Each node is visited exactly once.

      **Space Complexity:** O(1) — Only pointer variables used; existing nodes are reused.

      We use a dummy node to avoid special-casing the head. In each iteration, we attach the smaller of the two current nodes and advance that list's pointer. Finally, we attach any remaining nodes and return `dummy.next`.

  - approach_name: Recursive
    is_optimal: false
    code: |
      def merge_two_lists(
          list1: ListNode | None,
          list2: ListNode | None,
      ) -> ListNode | None:
          # Base cases: if either list is empty, return the other
          if not list1:
              return list2
          if not list2:
              return list1

          # Recursive case: attach smaller head and recurse
          if list1.val <= list2.val:
              # list1 is smaller, it becomes head of result
              list1.next = merge_two_lists(list1.next, list2)
              return list1
          else:
              # list2 is smaller, it becomes head of result
              list2.next = merge_two_lists(list1, list2.next)
              return list2
    explanation: |
      **Time Complexity:** O(n + m) — Each node is processed once.

      **Space Complexity:** O(n + m) — Recursion stack depth equals total number of nodes.

      This elegant recursive solution chooses the smaller head, then recursively merges the rest. The base case handles empty lists. While beautiful, the iterative approach is preferred for large lists due to stack space limitations.
