title: Array With Elements Not Equal to Average of Neighbors
slug: array-with-elements-not-equal-to-average-of-neighbors
difficulty: medium
leetcode_id: 1968
leetcode_url: https://leetcode.com/problems/array-with-elements-not-equal-to-average-of-neighbors/
categories:
  - arrays
  - sorting
patterns:
  - slug: greedy
    is_optimal: true

function_signature: "def rearrange_array(nums: list[int]) -> list[int]:"

test_cases:
  visible:
    - input: { nums: [1, 2, 3, 4, 5] }
      expected: [1, 2, 4, 5, 3]
    - input: { nums: [6, 2, 0, 9, 7] }
      expected: [9, 7, 6, 2, 0]
  hidden:
    - input: { nums: [1, 2, 3] }
      expected: [1, 3, 2]
    - input: { nums: [10, 20, 30, 40] }
      expected: [10, 30, 20, 40]
    - input: { nums: [5, 10, 15, 20, 25, 30] }
      expected: [5, 15, 10, 25, 20, 30]
    - input: { nums: [100, 200, 300] }
      expected: [100, 300, 200]
    - input: { nums: [1, 3, 5, 7, 9, 11, 13] }
      expected: [1, 5, 3, 9, 7, 13, 11]

description: |
  You are given a **0-indexed** array `nums` of **distinct** integers. You want to rearrange the elements in the array such that every element in the rearranged array is **not** equal to the **average** of its neighbors.

  More formally, the rearranged array should have the property such that for every `i` in the range `1 <= i < nums.length - 1`, `(nums[i-1] + nums[i+1]) / 2` is **not** equal to `nums[i]`.

  Return *any rearrangement of* `nums` *that meets the requirements*.

constraints: |
  - `3 <= nums.length <= 10^5`
  - `0 <= nums[i] <= 10^5`
  - All elements in `nums` are **distinct**

examples:
  - input: "nums = [1,2,3,4,5]"
    output: "[1,2,4,5,3]"
    explanation: "When i=1, nums[i] = 2, and the average of its neighbors is (1+4) / 2 = 2.5. When i=2, nums[i] = 4, and the average of its neighbors is (2+5) / 2 = 3.5. When i=3, nums[i] = 5, and the average of its neighbors is (4+3) / 2 = 3.5."
  - input: "nums = [6,2,0,9,7]"
    output: "[9,7,6,2,0]"
    explanation: "When i=1, nums[i] = 7, and the average of its neighbors is (9+6) / 2 = 7.5. When i=2, nums[i] = 6, and the average of its neighbors is (7+2) / 2 = 4.5. When i=3, nums[i] = 2, and the average of its neighbors is (6+0) / 2 = 3."

explanation:
  intuition: |
    The key insight is recognising when an element equals the average of its neighbours. If `nums[i] = (nums[i-1] + nums[i+1]) / 2`, then rearranging gives us `2 * nums[i] = nums[i-1] + nums[i+1]`. This only happens when the middle element is **exactly between** its neighbours numerically.

    Think of it like arranging people by height in a line: you want to avoid having anyone stand between two others where they're exactly the average height of those two. The simplest way to guarantee this? Create a **zigzag pattern** — make sure each person is either taller than both neighbours or shorter than both neighbours.

    This is called a **wiggle arrangement**: `nums[0] < nums[1] > nums[2] < nums[3] > nums[4] ...` (or the opposite pattern). When every element is a local maximum or local minimum, it can never be the average of its neighbours because it's never *between* them — it's always above or below both.

    The elegant solution: sort the array, then **interleave** elements from the smaller half and larger half. Placing small and large elements alternately guarantees the zigzag pattern.

  approach: |
    We solve this using a **Sort and Interleave** approach:

    **Step 1: Sort the array**

    - Sort `nums` in ascending order
    - This separates the values into a "small half" and a "large half"

    &nbsp;

    **Step 2: Split into two halves**

    - Small half: elements from index `0` to `(n-1)/2` (ceiling division)
    - Large half: elements from index `(n+1)/2` to end
    - For odd-length arrays, the small half gets the extra element

    &nbsp;

    **Step 3: Interleave the halves**

    - Create the result by alternating: `small[0], large[0], small[1], large[1], ...`
    - Place elements from the small half at even indices (0, 2, 4, ...)
    - Place elements from the large half at odd indices (1, 3, 5, ...)

    &nbsp;

    **Step 4: Return the result**

    - The interleaved array guarantees no element equals the average of its neighbours

    &nbsp;

    **Why does interleaving work?**

    After sorting and splitting, every element in the large half is strictly greater than every element in the small half (since all elements are distinct). When we interleave:
    - Each "small" element has "large" neighbours (both bigger)
    - Each "large" element has "small" neighbours (both smaller)

    An element can only be the average of its neighbours if it's between them. But in our arrangement, every element is either greater than both neighbours or less than both — never between.

  common_pitfalls:
    - title: Random Shuffling
      description: |
        A tempting approach is to randomly shuffle until you find a valid arrangement. While this might work for small inputs, it has no guarantee of termination and could take extremely long for larger arrays.

        With `n = 10^5` elements and many possible arrangements, random shuffling is not a reliable algorithm.
      wrong_approach: "Randomly shuffle and check validity"
      correct_approach: "Use deterministic sort + interleave"

    - title: Missing the Wiggle Pattern Insight
      description: |
        Without recognising that a wiggle pattern (local maxima/minima alternating) solves the problem, you might try complex approaches like checking each position individually or using backtracking.

        The key mathematical insight: `nums[i] = avg(nums[i-1], nums[i+1])` means `nums[i]` is exactly between its neighbours. A wiggle pattern ensures every element is above or below both neighbours — never between.
      wrong_approach: "Try to place elements one by one with backtracking"
      correct_approach: "Ensure wiggle pattern via sort + interleave"

    - title: Incorrect Half Split
      description: |
        When splitting the sorted array, be careful with odd-length arrays. If you split incorrectly, the interleaving won't work properly.

        For `n = 5`: small half should be indices 0, 1, 2 (3 elements) and large half should be indices 3, 4 (2 elements). This ensures we have enough small elements for even positions.
      wrong_approach: "Always split at n/2 without considering odd lengths"
      correct_approach: "Use ceiling division for small half size"

  key_takeaways:
    - "**Wiggle sort pattern**: Arranging elements so each is a local max or min (alternating) prevents any element from being the average of neighbours"
    - "**Sort + interleave technique**: Sorting and interleaving halves is a powerful way to create alternating high-low patterns"
    - "**Mathematical insight**: `nums[i] = (a + b) / 2` only when `nums[i]` is exactly between `a` and `b` — wiggle patterns avoid this"
    - "**Related problems**: Wiggle Sort, Wiggle Sort II, and Rearrange Array Elements by Sign use similar interleaving ideas"

  time_complexity: "O(n log n). Dominated by the sorting step. The interleaving is O(n)."
  space_complexity: "O(n). We create a new result array to store the interleaved elements."

solutions:
  - approach_name: Sort and Interleave
    is_optimal: true
    code: |
      def rearrange_array(nums: list[int]) -> list[int]:
          # Sort to separate small and large values
          nums.sort()
          n = len(nums)
          result = [0] * n

          # Split into two halves
          # Small half goes to even indices, large half to odd indices
          small_idx = 0
          large_idx = (n + 1) // 2  # Start of large half

          for i in range(n):
              if i % 2 == 0:
                  # Even index: place from small half
                  result[i] = nums[small_idx]
                  small_idx += 1
              else:
                  # Odd index: place from large half
                  result[i] = nums[large_idx]
                  large_idx += 1

          return result
    explanation: |
      **Time Complexity:** O(n log n) — Sorting dominates; interleaving is O(n).

      **Space Complexity:** O(n) — We allocate a result array of size n.

      By sorting and interleaving, we guarantee that elements at even indices (from the smaller half) are always less than their odd-index neighbours (from the larger half). This creates a wiggle pattern where no element can be the average of its neighbours.

  - approach_name: In-Place Swap (Greedy)
    is_optimal: false
    code: |
      def rearrange_array(nums: list[int]) -> list[int]:
          n = len(nums)

          # We want: nums[0] < nums[1] > nums[2] < nums[3] > ...
          for i in range(1, n):
              # At odd indices, we want a local max
              if i % 2 == 1:
                  if nums[i] < nums[i - 1]:
                      nums[i], nums[i - 1] = nums[i - 1], nums[i]
              # At even indices (except 0), we want a local min
              else:
                  if nums[i] > nums[i - 1]:
                      nums[i], nums[i - 1] = nums[i - 1], nums[i]

          return nums
    explanation: |
      **Time Complexity:** O(n) — Single pass through the array.

      **Space Complexity:** O(1) — Swaps are done in-place.

      This greedy approach iterates once, swapping adjacent elements whenever they violate the wiggle pattern. At odd indices we want a local maximum (greater than previous), at even indices we want a local minimum (less than previous). While this is O(n) time, it modifies the input array. The sort + interleave approach is clearer and avoids mutation.

  - approach_name: Sort Descending and Interleave
    is_optimal: false
    code: |
      def rearrange_array(nums: list[int]) -> list[int]:
          # Sort in descending order
          nums.sort(reverse=True)
          n = len(nums)
          result = []

          left = 0
          right = n - 1

          # Alternate between largest remaining and smallest remaining
          while left <= right:
              result.append(nums[left])  # Add large element
              left += 1
              if left <= right:
                  result.append(nums[right])  # Add small element
                  right -= 1

          return result
    explanation: |
      **Time Complexity:** O(n log n) — Sorting dominates.

      **Space Complexity:** O(n) — Result array storage.

      This variation sorts descending and alternates picking from the front (large) and back (small). It achieves the same wiggle effect with a different interleaving pattern: large-small-large-small instead of small-large-small-large. Both are valid solutions.
