Facebook Pixel

324. Wiggle Sort II

Problem Description

You are given an integer array nums. Your task is to reorder the array elements in-place so that they follow a "wiggle" pattern where:

  • The element at even index is smaller than the element at the next odd index
  • The element at odd index is greater than the element at the next even index

In other words, the pattern should be: nums[0] < nums[1] > nums[2] < nums[3] > nums[4] < nums[5]...

The problem guarantees that the input array will always have a valid answer, meaning it's always possible to arrange the elements to satisfy this wiggle pattern.

The solution approach sorts the array first, then strategically places elements from two different portions of the sorted array. It uses two pointers: one starting from the middle of the sorted array moving backward (for even indices), and another starting from the end of the sorted array moving backward (for odd indices). This ensures that smaller elements from the first half are placed at even positions, and larger elements from the second half are placed at odd positions, creating the required wiggle pattern.

For example, if the sorted array is [1, 2, 3, 4, 5, 6], the algorithm would place elements like:

  • Even indices (0, 2, 4): get elements from position (n-1)/2 backward: 3, 2, 1
  • Odd indices (1, 3, 5): get elements from position n-1 backward: 6, 5, 4
  • Result: [3, 6, 2, 5, 1, 4] which satisfies 3 < 6 > 2 < 5 > 1 < 4
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

To create a wiggle pattern where elements alternate between being smaller and larger than their neighbors, we need to carefully think about how to distribute the values in the array.

The key insight is that if we sort the array first, we can divide it into two halves: smaller elements and larger elements. To avoid having equal adjacent elements (which would violate the strict inequality requirement), we need to interleave these two halves in a specific way.

Why does simply alternating between small and large halves work? Consider a sorted array like [1, 2, 3, 4, 5, 6]. If we naively take elements from the beginning of each half ([1, 2, 3] and [4, 5, 6]) and alternate them, we might get [1, 4, 2, 5, 3, 6]. This works, but there's a subtle issue: if we have duplicate values around the middle (like [1, 2, 3, 3, 4, 5]), placing elements from the start of each half could put equal values next to each other.

The clever trick is to take elements from the end of each half, moving backward. By taking from the middle position (n-1)/2 backward for even indices and from the last position n-1 backward for odd indices, we maximize the "distance" between selected values. This ensures that:

  • Elements at even positions come from the smaller half (but starting from its largest values)
  • Elements at odd positions come from the larger half (starting from its largest values)
  • Adjacent elements are as far apart as possible in the sorted order, minimizing the chance of placing equal values next to each other

This backward traversal strategy guarantees that we create valid "peaks" and "valleys" in the wiggle pattern, with each peak being strictly greater than its neighboring valleys, and each valley being strictly less than its neighboring peaks.

Solution Approach

The implementation follows a three-step process:

Step 1: Sort the array

arr = sorted(nums)

We create a sorted copy of the input array. This allows us to identify which elements should go into the "smaller" half and which into the "larger" half.

Step 2: Initialize two pointers

i, j = (n - 1) >> 1, n - 1
  • Pointer i starts at the middle index (n-1) >> 1 (equivalent to (n-1) // 2). This pointer will traverse the first half of the sorted array backward.
  • Pointer j starts at the last index n - 1. This pointer will traverse the second half of the sorted array backward.

Step 3: Place elements alternately

for k in range(n):
    if k % 2 == 0:
        nums[k] = arr[i]
        i -= 1
    else:
        nums[k] = arr[j]
        j -= 1

We iterate through each position in the original array:

  • For even indices (k % 2 == 0): Place elements from the first half by using pointer i, then decrement i
  • For odd indices: Place elements from the second half by using pointer j, then decrement j

Example walkthrough: For nums = [1, 5, 1, 1, 6, 4]:

  1. After sorting: arr = [1, 1, 1, 4, 5, 6]
  2. Initialize: i = 2 (pointing to arr[2] = 1), j = 5 (pointing to arr[5] = 6)
  3. Fill positions:
    • k=0 (even): nums[0] = arr[2] = 1, i becomes 1
    • k=1 (odd): nums[1] = arr[5] = 6, j becomes 4
    • k=2 (even): nums[2] = arr[1] = 1, i becomes 0
    • k=3 (odd): nums[3] = arr[4] = 5, j becomes 3
    • k=4 (even): nums[4] = arr[0] = 1, i becomes -1
    • k=5 (odd): nums[5] = arr[3] = 4, j becomes 2
  4. Result: nums = [1, 6, 1, 5, 1, 4] which satisfies 1 < 6 > 1 < 5 > 1 < 4

The time complexity is O(n log n) due to sorting, and the space complexity is O(n) for storing the sorted array.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example with nums = [3, 5, 2, 1, 6, 4] to illustrate the solution approach.

Step 1: Sort the array First, we create a sorted copy:

arr = [1, 2, 3, 4, 5, 6]

Step 2: Initialize two pointers

n = 6
i = (6-1) >> 1 = 2  (points to arr[2] = 3)
j = 6-1 = 5         (points to arr[5] = 6)

Step 3: Place elements alternately Now we fill each position in the original array:

kk%2ActionElement placedijnums so far
00 (even)Use arr[i=2]315[3, _, _, _, _, _]
11 (odd)Use arr[j=5]614[3, 6, _, _, _, _]
20 (even)Use arr[i=1]204[3, 6, 2, _, _, _]
31 (odd)Use arr[j=4]503[3, 6, 2, 5, _, _]
40 (even)Use arr[i=0]1-13[3, 6, 2, 5, 1, _]
51 (odd)Use arr[j=3]4-12[3, 6, 2, 5, 1, 4]

Final result: nums = [3, 6, 2, 5, 1, 4]

Verification of wiggle pattern:

  • nums[0] < nums[1]: 3 < 6 βœ“
  • nums[1] > nums[2]: 6 > 2 βœ“
  • nums[2] < nums[3]: 2 < 5 βœ“
  • nums[3] > nums[4]: 5 > 1 βœ“
  • nums[4] < nums[5]: 1 < 4 βœ“

The key insight here is that by taking elements from the end of each half moving backward, we ensure maximum separation between adjacent elements. The smaller half contributes {3, 2, 1} to even positions, while the larger half contributes {6, 5, 4} to odd positions, creating the required alternating pattern of peaks and valleys.

Solution Implementation

1class Solution:
2    def wiggleSort(self, nums: List[int]) -> None:
3        """
4        Do not return anything, modify nums in-place instead.
5        """
6        # Create a sorted copy of the input array
7        sorted_array = sorted(nums)
8        n = len(sorted_array)
9      
10        # Initialize two pointers:
11        # left_pointer: points to the middle element (for smaller half)
12        # right_pointer: points to the last element (for larger half)
13        left_pointer = (n - 1) // 2
14        right_pointer = n - 1
15      
16        # Reconstruct the array in wiggle pattern
17        # Even indices get elements from the smaller half (backwards from middle)
18        # Odd indices get elements from the larger half (backwards from end)
19        for index in range(n):
20            if index % 2 == 0:
21                # Place smaller elements at even positions
22                nums[index] = sorted_array[left_pointer]
23                left_pointer -= 1
24            else:
25                # Place larger elements at odd positions
26                nums[index] = sorted_array[right_pointer]
27                right_pointer -= 1
28
1class Solution {
2    /**
3     * Rearranges the array into a wiggle pattern where nums[0] < nums[1] > nums[2] < nums[3]...
4     * Algorithm:
5     * 1. Create a sorted copy of the input array
6     * 2. Split conceptually into two halves: smaller half and larger half
7     * 3. Place elements from each half alternately, starting with smaller half
8     * 4. Traverse both halves from right to left to avoid equal adjacent elements
9     * 
10     * @param nums the input array to be rearranged in-place
11     */
12    public void wiggleSort(int[] nums) {
13        // Create a copy of the original array and sort it
14        int[] sortedArray = nums.clone();
15        Arrays.sort(sortedArray);
16      
17        // Get the length of the array
18        int length = nums.length;
19      
20        // Calculate the middle index (end of smaller half)
21        // For odd length, smaller half has one more element
22        int middleIndex = (length - 1) >> 1;  // Equivalent to (length - 1) / 2
23      
24        // Index for the larger half (starts from the last element)
25        int largerHalfIndex = length - 1;
26      
27        // Fill the original array with wiggle pattern
28        for (int currentIndex = 0; currentIndex < length; currentIndex++) {
29            if (currentIndex % 2 == 0) {
30                // Even indices get elements from the smaller half (traversing right to left)
31                nums[currentIndex] = sortedArray[middleIndex];
32                middleIndex--;
33            } else {
34                // Odd indices get elements from the larger half (traversing right to left)
35                nums[currentIndex] = sortedArray[largerHalfIndex];
36                largerHalfIndex--;
37            }
38        }
39    }
40}
41
1class Solution {
2public:
3    void wiggleSort(vector<int>& nums) {
4        // Create a copy of the original array
5        vector<int> sortedArray = nums;
6      
7        // Sort the copied array in ascending order
8        sort(sortedArray.begin(), sortedArray.end());
9      
10        // Get the size of the array
11        int arraySize = nums.size();
12      
13        // Initialize two pointers:
14        // leftPointer: points to the middle element (for smaller half)
15        // rightPointer: points to the last element (for larger half)
16        int leftPointer = (arraySize - 1) / 2;
17        int rightPointer = arraySize - 1;
18      
19        // Reconstruct the array in wiggle pattern
20        // Even indices get elements from the smaller half (in reverse order)
21        // Odd indices get elements from the larger half (in reverse order)
22        for (int index = 0; index < arraySize; ++index) {
23            if (index % 2 == 0) {
24                // Even index: pick from smaller half
25                nums[index] = sortedArray[leftPointer];
26                leftPointer--;
27            } else {
28                // Odd index: pick from larger half
29                nums[index] = sortedArray[rightPointer];
30                rightPointer--;
31            }
32        }
33    }
34};
35
1/**
2 * Wiggle sort function that rearranges the array such that nums[0] < nums[1] > nums[2] < nums[3]...
3 * Uses bucket sort approach to achieve the wiggle pattern
4 * @param nums - The input array to be sorted in wiggle pattern
5 * @return void - Modifies the array in-place
6 */
7function wiggleSort(nums: number[]): void {
8    // Create a bucket array to count frequency of each number (0 to 5000)
9    const bucket: number[] = new Array(5001).fill(0);
10  
11    // Count the frequency of each number in the input array
12    for (const value of nums) {
13        bucket[value]++;
14    }
15  
16    const arrayLength: number = nums.length;
17    let currentIndex: number = 5000;
18  
19    // Fill odd indices with larger numbers from the bucket (descending order)
20    // This ensures nums[1], nums[3], nums[5]... get larger values
21    for (let i = 1; i < arrayLength; i += 2) {
22        // Skip empty buckets
23        while (bucket[currentIndex] === 0) {
24            currentIndex--;
25        }
26        nums[i] = currentIndex;
27        bucket[currentIndex]--;
28    }
29  
30    // Fill even indices with remaining numbers from the bucket (still descending)
31    // This ensures nums[0], nums[2], nums[4]... get smaller values than odd indices
32    for (let i = 0; i < arrayLength; i += 2) {
33        // Skip empty buckets
34        while (bucket[currentIndex] === 0) {
35            currentIndex--;
36        }
37        nums[i] = currentIndex;
38        bucket[currentIndex]--;
39    }
40}
41

Time and Space Complexity

Time Complexity: O(n log n)

The dominant operation in this algorithm is the sorting step arr = sorted(nums), which takes O(n log n) time where n is the length of the input array. After sorting, the algorithm performs a single pass through the array to rearrange elements, which takes O(n) time. Therefore, the overall time complexity is O(n log n) + O(n) = O(n log n).

Space Complexity: O(n)

The algorithm creates a sorted copy of the input array arr = sorted(nums), which requires O(n) additional space to store all n elements. The remaining variables (i, j, k, n) use only O(1) constant space. Therefore, the total space complexity is O(n).

Common Pitfalls

Pitfall: Failing to Handle Duplicate Elements Correctly

The most critical pitfall in this problem occurs when the array contains many duplicate elements, especially when duplicates appear around the median value. Simply placing elements from the sorted array's two halves might create adjacent equal elements, violating the strict inequality requirement of the wiggle pattern.

Problem Example: Consider nums = [4, 5, 5, 6]:

  • Sorted: [4, 5, 5, 6]
  • Using a naive approach might produce: [5, 6, 4, 5]
  • This violates the pattern at indices 2-3: 4 < 5 but we need 4 > 5

Why This Happens: When duplicate values exist near the median, they can end up in both the "smaller" and "larger" halves. The backward traversal strategy helps mitigate this by maximizing the distance between where duplicates are placed, but understanding this is crucial.

Solution: Understanding the Backward Traversal Strategy

The algorithm deliberately traverses both halves backward to create maximum separation between potential duplicate values:

def wiggleSort(self, nums: List[int]) -> None:
    sorted_array = sorted(nums)
    n = len(sorted_array)
  
    # Key insight: Start from the rightmost elements of each half
    # This maximizes distance between duplicates
    mid = (n - 1) // 2  # Last index of smaller half
    end = n - 1         # Last index of larger half
  
    # Fill the array alternating between halves, going backward
    for i in range(n):
        if i % 2 == 0:
            nums[i] = sorted_array[mid]
            mid -= 1
        else:
            nums[i] = sorted_array[end]
            end -= 1

Visual Example with Duplicates: For nums = [1, 1, 2, 2, 3, 3]:

  • Sorted: [1, 1, 2, 2, 3, 3]
  • Mid pointer starts at index 2 (value 2)
  • End pointer starts at index 5 (value 3)
  • Result: [2, 3, 1, 3, 1, 2] βœ“ (satisfies wiggle pattern)

If we had used forward traversal instead:

  • Would produce: [1, 2, 1, 2, 2, 3] βœ— (fails at indices 3-4: 2 = 2)

Additional Considerations:

  1. Array Length: The algorithm works correctly for both even and odd length arrays due to the (n-1)//2 calculation for the middle index.

  2. Edge Case - All Same Elements: While the problem guarantees a valid answer exists, arrays with all identical elements (like [5, 5, 5, 5]) cannot form a valid wiggle pattern. The problem statement ensures this won't occur.

  3. Alternative O(n) Solution: For those seeking optimal time complexity, there exists a more complex O(n) solution using virtual indexing and three-way partitioning (Dutch National Flag algorithm variant), but it's significantly harder to implement and understand.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

How does merge sort divide the problem into subproblems?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More