title: Array of Doubled Pairs
slug: array-of-doubled-pairs
difficulty: medium
leetcode_id: 954
leetcode_url: https://leetcode.com/problems/array-of-doubled-pairs/
categories:
  - arrays
  - hash-tables
  - sorting
patterns:
  - greedy

function_signature: "def can_reorder_doubled(arr: list[int]) -> bool:"

test_cases:
  visible:
    - input: { arr: [3, 1, 3, 6] }
      expected: false
    - input: { arr: [2, 1, 2, 6] }
      expected: false
    - input: { arr: [4, -2, 2, -4] }
      expected: true
  hidden:
    - input: { arr: [1, 2, 4, 8] }
      expected: true
    - input: { arr: [0, 0] }
      expected: true
    - input: { arr: [0, 0, 0, 0] }
      expected: true
    - input: { arr: [-2, -4] }
      expected: true
    - input: { arr: [-1, -2, -4, -8] }
      expected: true
    - input: { arr: [1, 2, 1, 2] }
      expected: true
    - input: { arr: [1, 3] }
      expected: false

description: |
  Given an integer array of even length `arr`, return `true` *if it is possible to reorder* `arr` *such that* `arr[2 * i + 1] = 2 * arr[2 * i]` *for every* `0 <= i < len(arr) / 2`, *or* `false` *otherwise*.

  In other words, you need to check if the array can be rearranged into pairs where one element is exactly double the other.

constraints: |
  - `2 <= arr.length <= 3 * 10^4`
  - `arr.length` is even
  - `-10^5 <= arr[i] <= 10^5`

examples:
  - input: "arr = [3,1,3,6]"
    output: "false"
    explanation: "No valid pairing exists. The pairs would need to be (3,6) and (1,?), but there's no 2 in the array."
  - input: "arr = [2,1,2,6]"
    output: "false"
    explanation: "We could pair (1,2) but then we'd have [2,6] left, and 6 != 2*2."
  - input: "arr = [4,-2,2,-4]"
    output: "true"
    explanation: "We can form pairs (-2,-4) and (2,4) to create [-2,-4,2,4] or [2,4,-2,-4]."

explanation:
  intuition: |
    Think of this problem like matching socks — except each "sock" must find its exact double (or half).

    The key insight is that **order matters when pairing**. If you try to pair numbers randomly, you might "use up" a number that was needed elsewhere. For example, with `[1, 2, 4, 8]`, if you greedily pair `2` with `4`, you've broken the valid pairing of `(1,2)` and `(4,8)`.

    The solution is to **process numbers by absolute value from smallest to largest**. Why? Because a smaller number can only be the "base" of a pair (the number that gets doubled), never the "double" of something even smaller that we haven't seen yet. By handling small numbers first, we ensure each number finds its correct partner.

    For negative numbers, the logic reverses: `-4` doubled is `-8`, so we still process by absolute value but the "double" is actually more negative.

  approach: |
    We solve this using a **Greedy + Hash Map Approach**:

    **Step 1: Count occurrences of each number**

    - Use a hash map (Counter) to count how many times each number appears
    - This allows O(1) lookup when searching for doubles

    &nbsp;

    **Step 2: Sort numbers by absolute value**

    - Process numbers from smallest absolute value to largest
    - This ensures we always try to pair a number with its double (not half)
    - For negatives like `-2`, its double `-4` has larger absolute value, so `-2` is processed first

    &nbsp;

    **Step 3: Greedily pair each number with its double**

    - For each number `x` in sorted order:
      - If `count[x] == 0`, skip (already used as someone's double)
      - If `count[2*x] == 0`, return `false` (no double available)
      - Otherwise, decrement both `count[x]` and `count[2*x]`

    &nbsp;

    **Step 4: Return the result**

    - If we successfully pair all numbers, return `true`

  common_pitfalls:
    - title: Processing in Wrong Order
      description: |
        If you process numbers in regular sorted order (not by absolute value), negative numbers break the logic.

        For example, with `[-4, -2, 2, 4]`:
        - Regular sort: `[-4, -2, 2, 4]`
        - Processing `-4` first, its double is `-8` which doesn't exist!

        But `-4` should be the *double* of `-2`, not the base. Sorting by absolute value gives `[-2, 2, -4, 4]`, so `-2` correctly pairs with `-4`.
      wrong_approach: "Sort by value"
      correct_approach: "Sort by absolute value"

    - title: Not Handling Zero Correctly
      description: |
        Zero is its own double (`0 * 2 = 0`). This means zeros must appear in pairs.

        If you have an odd count of zeros, it's impossible to form valid pairs. Your counting approach handles this naturally — pairing `0` with `2*0 = 0` decrements the count by 2 each time.
      wrong_approach: "Special-case zero handling"
      correct_approach: "Let the general algorithm handle 0 paired with 0"

    - title: Modifying Array While Iterating
      description: |
        Some approaches try to remove elements from the array as they're paired. This leads to index shifting bugs and O(n) removal costs.

        Using a count map and decrementing counts is cleaner and more efficient.
      wrong_approach: "Remove elements from array"
      correct_approach: "Use count map and decrement"

  key_takeaways:
    - "**Process by absolute value** when dealing with doubling/halving that involves negatives"
    - "**Greedy works when ordering eliminates conflicts** — smallest-first ensures each number can only be a base, never a double"
    - "**Count maps** are powerful for pairing problems — O(1) lookup beats O(n) search"
    - "This pattern extends to problems like *Array of K Pairs* or finding pairs with any fixed ratio"

  time_complexity: "O(n log n). Sorting dominates; the pairing loop is O(n) with O(1) hash map operations."
  space_complexity: "O(n). The count map stores at most `n` unique elements."

solutions:
  - approach_name: Greedy with Sorting by Absolute Value
    is_optimal: true
    code: |
      from collections import Counter

      def can_reorder_doubled(arr: list[int]) -> bool:
          # Count occurrences of each number
          count = Counter(arr)

          # Sort by absolute value - process smaller magnitudes first
          for x in sorted(arr, key=abs):
              # Skip if this number was already used as someone's double
              if count[x] == 0:
                  continue

              # Check if the double exists
              if count[2 * x] == 0:
                  return False  # No valid pair for x

              # Pair x with 2*x: decrement both counts
              count[x] -= 1
              count[2 * x] -= 1

          return True
    explanation: |
      **Time Complexity:** O(n log n) — Dominated by sorting. The iteration and hash map operations are O(n).

      **Space Complexity:** O(n) — The Counter stores up to n elements.

      By sorting by absolute value, we ensure that when we process a number `x`, its double `2*x` hasn't been incorrectly paired with something else. This greedy choice is locally and globally optimal.

  - approach_name: Brute Force with Backtracking
    is_optimal: false
    code: |
      def can_reorder_doubled(arr: list[int]) -> bool:
          def backtrack(remaining: list[int]) -> bool:
              if not remaining:
                  return True  # All numbers paired successfully

              # Try to pair the first element with its double
              x = remaining[0]
              target = 2 * x

              for i in range(1, len(remaining)):
                  if remaining[i] == target:
                      # Found a valid pair, recurse with remaining elements
                      new_remaining = remaining[1:i] + remaining[i+1:]
                      if backtrack(new_remaining):
                          return True

              return False  # No valid pairing found

          return backtrack(arr)
    explanation: |
      **Time Complexity:** O(n! / 2^(n/2)) — Exponential due to trying all possible pairings.

      **Space Complexity:** O(n) — Recursion depth and list copies.

      This approach tries every possible way to pair elements. While correct, it's far too slow for the constraint `n <= 3 * 10^4`. Included to illustrate why the greedy approach is necessary.
