title: Beautiful Array
slug: beautiful-array
difficulty: medium
leetcode_id: 932
leetcode_url: https://leetcode.com/problems/beautiful-array/
categories:
  - arrays
  - math
patterns:
  - dynamic-programming

description: |
  An array `nums` of length `n` is **beautiful** if:

  - `nums` is a permutation of the integers in the range `[1, n]`.
  - For every `0 <= i < j < n`, there is no index `k` with `i < k < j` where `2 * nums[k] == nums[i] + nums[j]`.

  Given the integer `n`, return *any beautiful array* `nums` *of length* `n`. There will be at least one valid answer for the given `n`.

constraints: |
  - `1 <= n <= 1000`

examples:
  - input: "n = 4"
    output: "[2, 1, 4, 3]"
    explanation: "This is a beautiful array because no three elements at positions i < k < j satisfy 2 * nums[k] == nums[i] + nums[j]."
  - input: "n = 5"
    output: "[3, 1, 2, 5, 4]"
    explanation: "This permutation satisfies the beautiful array property — no arithmetic mean constraint is violated."

explanation:
  intuition: |
    The key insight is understanding what makes an array *not* beautiful: three elements `nums[i]`, `nums[k]`, `nums[j]` where `i < k < j` and `nums[k]` is the **arithmetic mean** of `nums[i]` and `nums[j]`.

    Think of it like this: if `2 * nums[k] == nums[i] + nums[j]`, then `nums[k]` lies exactly halfway between `nums[i]` and `nums[j]`. For this to happen, `nums[i] + nums[j]` must be **even**, which requires `nums[i]` and `nums[j]` to have the **same parity** (both odd or both even).

    Here's the breakthrough: if we arrange all **odd numbers** on one side and all **even numbers** on the other side, then for any `i < j` where `nums[i]` and `nums[j]` have different parities, the sum `nums[i] + nums[j]` is odd. An odd sum divided by 2 cannot be an integer, so there's no valid `nums[k]` that could violate the condition!

    But what about pairs with the same parity? We apply the same logic **recursively**. Within the odd section and within the even section, we recursively build beautiful sub-arrays. This divide-and-conquer approach ensures no violations exist at any level.

  approach: |
    We solve this using a **Divide and Conquer** approach based on parity separation:

    **Step 1: Understand the transformation properties**

    - If `A` is a beautiful array, then `2 * A - 1` (making all elements odd) is also beautiful
    - If `A` is a beautiful array, then `2 * A` (making all elements even) is also beautiful
    - Concatenating `[odd elements] + [even elements]` preserves the beautiful property across the join

    &nbsp;

    **Step 2: Build recursively with memoisation**

    - Base case: `n = 1` returns `[1]`
    - For larger `n`, recursively build beautiful arrays for `(n + 1) // 2` elements (odd positions) and `n // 2` elements (even positions)
    - Transform the first array to odd numbers: `2 * x - 1`
    - Transform the second array to even numbers: `2 * x`
    - Concatenate and filter to keep only values `<= n`

    &nbsp;

    **Step 3: Return the result**

    - The concatenation of transformed odd and even arrays gives us our beautiful array

    &nbsp;

    This works because the parity separation guarantees no arithmetic mean can exist between elements from different halves, and recursion ensures the same property within each half.

  common_pitfalls:
    - title: Brute Force Validation
      description: |
        A naive approach might try to generate random permutations and validate each one by checking all triplets `(i, k, j)`.

        With `n = 1000`, there are `n!` permutations and O(n^3) validation per permutation. This is astronomically slow and will never complete.
      wrong_approach: "Random permutation generation with triplet validation"
      correct_approach: "Constructive divide-and-conquer algorithm"

    - title: Missing the Parity Insight
      description: |
        Without recognising that `2 * nums[k] == nums[i] + nums[j]` requires `nums[i]` and `nums[j]` to have the same parity, you might not see the path to a solution.

        The sum of an odd and even number is always odd. Half of an odd number is not an integer, so it cannot equal any `nums[k]`.
      wrong_approach: "Trying complex arrangements without mathematical insight"
      correct_approach: "Separate by parity, then recurse"

    - title: Off-by-One Errors in Recursion
      description: |
        When splitting `n` elements into odd and even counts, be careful:
        - Odd count: `(n + 1) // 2` (ceiling division)
        - Even count: `n // 2` (floor division)

        After transformation, filter to keep only values `<= n` since the transformation might produce values outside the valid range.
      wrong_approach: "Incorrect ceiling/floor division"
      correct_approach: "Use `(n + 1) // 2` for odds, `n // 2` for evens, then filter"

  key_takeaways:
    - "**Parity separation**: When an arithmetic relationship requires an even sum, separating odds and evens breaks the relationship across the boundary"
    - "**Constructive algorithms**: Instead of searching for valid solutions, construct them using mathematical properties"
    - "**Affine transformations preserve structure**: If `A` is beautiful, then `k * A + c` is also beautiful for any constants `k`, `c`"
    - "**Divide and conquer with invariants**: The beautiful property is preserved through concatenation when the halves are parity-separated"

  time_complexity: "O(n log n). We build arrays of size roughly n/2 at each recursion level, with log n levels total."
  space_complexity: "O(n log n). The memoisation cache stores arrays for sizes 1 through n, with total elements proportional to n log n."

solutions:
  - approach_name: Divide and Conquer
    is_optimal: true
    code: |
      def beautiful_array(n: int) -> list[int]:
          # Memoisation to avoid recomputing the same sizes
          memo = {1: [1]}

          def build(size: int) -> list[int]:
              if size in memo:
                  return memo[size]

              # Recursively build smaller beautiful arrays
              # Odd positions: transform to 2*x - 1 (odd numbers)
              odds = build((size + 1) // 2)
              # Even positions: transform to 2*x (even numbers)
              evens = build(size // 2)

              # Transform and concatenate: all odds first, then all evens
              # This ensures no arithmetic mean exists across the boundary
              result = [2 * x - 1 for x in odds] + [2 * x for x in evens]

              # Filter to keep only values <= size
              result = [x for x in result if x <= size]

              memo[size] = result
              return result

          return build(n)
    explanation: |
      **Time Complexity:** O(n log n) — At each level of recursion, we process O(n) elements total, with O(log n) levels.

      **Space Complexity:** O(n log n) — The memoisation cache stores arrays of decreasing sizes, summing to O(n log n) total elements.

      The algorithm exploits that separating odds and evens prevents arithmetic means across the boundary. By recursively applying this within each half, we construct a beautiful array directly.

  - approach_name: Iterative Construction
    is_optimal: true
    code: |
      def beautiful_array(n: int) -> list[int]:
          # Start with the simplest beautiful array
          result = [1]

          # Build up by applying transformations
          while len(result) < n:
              # Odd transformation: 2*x - 1, then even: 2*x
              # Concatenate odds first, then evens
              result = [2 * x - 1 for x in result] + [2 * x for x in result]

          # Filter to keep only values in range [1, n]
          return [x for x in result if x <= n]
    explanation: |
      **Time Complexity:** O(n log n) — We double the array size in each iteration, performing O(n) work per iteration across O(log n) iterations.

      **Space Complexity:** O(n) — We only store the current result array.

      This iterative version builds bottom-up instead of top-down. Starting from `[1]`, we repeatedly transform to create odds and evens, doubling the size until we exceed `n`, then filter. This is equivalent to the recursive approach but uses less memory.
