280. Wiggle Sort π
Problem Description
Given an integer array nums
, you need to reorder it to follow a "wiggle" pattern where:
nums[0] <= nums[1] >= nums[2] <= nums[3] >= nums[4]...
In other words:
- Elements at even indices (0, 2, 4, ...) should be less than or equal to their next neighbor
- Elements at odd indices (1, 3, 5, ...) should be greater than or equal to their next neighbor
This creates an alternating pattern where the values go up, then down, then up, then down, like a wiggle.
For example:
- Input:
[3, 5, 2, 1, 6, 4]
- One valid output:
[3, 5, 1, 6, 2, 4]
- Position 0:
3 <= 5
β - Position 1:
5 >= 1
β - Position 2:
1 <= 6
β - Position 3:
6 >= 2
β - Position 4:
2 <= 4
β
- Position 0:
The problem guarantees that the input array will always have at least one valid answer. You need to modify the array in-place without returning anything.
Intuition
The key insight is that we don't need to sort the entire array to achieve the wiggle pattern. Instead, we can fix violations locally as we traverse the array.
Think about what makes a valid wiggle sort: at each position, we only care about the relationship between the current element and its immediate neighbor. If we're at an odd index, the current element should be greater than or equal to the previous one. If we're at an even index, the current element should be less than or equal to the previous one.
When we encounter a violation of this rule, we can simply swap the current element with the previous one. Why does this work?
Consider two cases:
-
At odd index
i
, ifnums[i] < nums[i-1]
(violation), swapping them makesnums[i] >= nums[i-1]
. This swap won't break the relationship ati-1
because:- At
i-1
(even index), we needednums[i-1] <= nums[i-2]
- After swap, the smaller value moves to
i-1
, sonums[i-1] <= nums[i-2]
still holds
- At
-
At even index
i
, ifnums[i] > nums[i-1]
(violation), swapping them makesnums[i] <= nums[i-1]
. Similarly, this maintains the validity at positioni-1
.
The beauty of this approach is its greediness - we make local corrections that guarantee global correctness. Each swap fixes the current position without breaking previous positions, allowing us to solve the problem in a single pass with O(n)
time and O(1)
space.
Solution Approach
The implementation uses a single-pass greedy algorithm to create the wiggle pattern. Here's how it works:
We iterate through the array starting from index 1, checking each element against its predecessor. At each position i
, we determine whether a swap is needed based on two conditions:
-
Odd index condition: If
i
is odd (1, 3, 5, ...), we neednums[i] >= nums[i-1]
. Ifnums[i] < nums[i-1]
, we swap them. -
Even index condition: If
i
is even (2, 4, 6, ...), we neednums[i] <= nums[i-1]
. Ifnums[i] > nums[i-1]
, we swap them.
The code combines these conditions into a single if statement:
if (i % 2 == 1 and nums[i] < nums[i - 1]) or (i % 2 == 0 and nums[i] > nums[i - 1]): nums[i], nums[i - 1] = nums[i - 1], nums[i]
Let's trace through an example with nums = [3, 5, 2, 1, 6, 4]
:
i = 1
(odd): Compare5
with3
. Since5 >= 3
, no swap needed. Array:[3, 5, 2, 1, 6, 4]
i = 2
(even): Compare2
with5
. Since2 < 5
, no swap needed. Array:[3, 5, 2, 1, 6, 4]
i = 3
(odd): Compare1
with2
. Since1 < 2
, swap them. Array:[3, 5, 1, 2, 6, 4]
i = 4
(even): Compare6
with2
. Since6 > 2
, swap them. Array:[3, 5, 1, 6, 2, 4]
i = 5
(odd): Compare4
with2
. Since4 > 2
, no swap needed. Final:[3, 5, 1, 6, 2, 4]
The algorithm modifies the array in-place with O(n)
time complexity and O(1)
space complexity, making it highly efficient.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach using the array [6, 3, 4, 2]
.
Initial array: [6, 3, 4, 2]
We want to achieve the wiggle pattern: nums[0] <= nums[1] >= nums[2] <= nums[3]...
Step 1: i = 1
(odd index)
- We need
nums[1] >= nums[0]
- Check:
3 >= 6
? No,3 < 6
(violation!) - Action: Swap
nums[1]
andnums[0]
- Array becomes:
[3, 6, 4, 2]
Step 2: i = 2
(even index)
- We need
nums[2] <= nums[1]
- Check:
4 <= 6
? Yes (no violation) - Action: No swap needed
- Array remains:
[3, 6, 4, 2]
Step 3: i = 3
(odd index)
- We need
nums[3] >= nums[2]
- Check:
2 >= 4
? No,2 < 4
(violation!) - Action: Swap
nums[3]
andnums[2]
- Array becomes:
[3, 6, 2, 4]
Final result: [3, 6, 2, 4]
Let's verify the wiggle pattern:
- Position 0:
3 <= 6
β - Position 1:
6 >= 2
β - Position 2:
2 <= 4
β
The key insight is that each swap fixes the current position's violation while preserving the correctness of previous positions. When we swap at an odd index, we move the larger value to that position (satisfying the >= requirement), and the smaller value goes to the previous even position (which needed a <= relationship). This greedy approach guarantees we achieve a valid wiggle pattern in just one pass through the array.
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 Rearranges the array so that nums[0] <= nums[1] >= nums[2] <= nums[3]...
7 This creates a "wiggle" pattern where elements alternate between being
8 less than or equal to and greater than or equal to their neighbors.
9 """
10 # Iterate through the array starting from index 1
11 for i in range(1, len(nums)):
12 # Check if current position violates the wiggle pattern
13 # Odd indices should be peaks (greater than or equal to previous)
14 # Even indices should be valleys (less than or equal to previous)
15
16 if (i % 2 == 1 and nums[i] < nums[i - 1]) or \
17 (i % 2 == 0 and nums[i] > nums[i - 1]):
18 # Swap current element with previous element to fix the pattern
19 nums[i], nums[i - 1] = nums[i - 1], nums[i]
20
1class Solution {
2 /**
3 * Sorts the array into a wiggle pattern where nums[0] <= nums[1] >= nums[2] <= nums[3]...
4 * The pattern alternates between less than or equal to and greater than or equal to.
5 *
6 * @param nums the input array to be sorted in wiggle pattern
7 */
8 public void wiggleSort(int[] nums) {
9 // Iterate through the array starting from index 1
10 for (int i = 1; i < nums.length; i++) {
11 // Check if current position violates the wiggle pattern
12 // For odd indices: current element should be >= previous element
13 // For even indices: current element should be <= previous element
14 if ((i % 2 == 1 && nums[i] < nums[i - 1]) ||
15 (i % 2 == 0 && nums[i] > nums[i - 1])) {
16 // Swap current element with previous element to fix the pattern
17 swap(nums, i, i - 1);
18 }
19 }
20 }
21
22 /**
23 * Helper method to swap two elements in the array
24 *
25 * @param nums the array containing elements to swap
26 * @param i index of the first element
27 * @param j index of the second element
28 */
29 private void swap(int[] nums, int i, int j) {
30 int temp = nums[i];
31 nums[i] = nums[j];
32 nums[j] = temp;
33 }
34}
35
1class Solution {
2public:
3 void wiggleSort(vector<int>& nums) {
4 // Iterate through the array starting from index 1
5 for (int i = 1; i < nums.size(); ++i) {
6 // Check if current position violates wiggle pattern
7 // Odd indices should be peaks (greater than previous element)
8 // Even indices should be valleys (less than previous element)
9 if ((i % 2 == 1 && nums[i] < nums[i - 1]) ||
10 (i % 2 == 0 && nums[i] > nums[i - 1])) {
11 // Swap elements to maintain wiggle pattern
12 swap(nums[i], nums[i - 1]);
13 }
14 }
15 }
16};
17
1function wiggleSort(nums: number[]): void {
2 // Iterate through the array starting from index 1
3 for (let i = 1; i < nums.length; i++) {
4 // Check if current position violates wiggle pattern
5 // Odd indices should be peaks (greater than previous element)
6 // Even indices should be valleys (less than previous element)
7 if ((i % 2 === 1 && nums[i] < nums[i - 1]) ||
8 (i % 2 === 0 && nums[i] > nums[i - 1])) {
9 // Swap elements to maintain wiggle pattern
10 // Store the current element in a temporary variable
11 const temp = nums[i];
12 nums[i] = nums[i - 1];
13 nums[i - 1] = temp;
14 }
15 }
16}
17
Time and Space Complexity
Time Complexity: O(n)
where n
is the length of the input array nums
. The algorithm makes a single pass through the array starting from index 1, performing constant-time operations (comparison and swap) at each position. Each element is visited exactly once, resulting in linear time complexity.
Space Complexity: O(1)
. The algorithm modifies the array in-place and only uses a constant amount of extra space for the loop variable i
and temporary variables during the swap operation. No additional data structures are created that depend on the input size.
Common Pitfalls
Pitfall 1: Confusing Index Positions with Element Values
A common mistake is misunderstanding which indices should be "peaks" and which should be "valleys" in the wiggle pattern. Developers often incorrectly assume:
- Even indices (0, 2, 4, ...) should be greater than their neighbors
- Odd indices (1, 3, 5, ...) should be less than their neighbors
This is backwards! The correct pattern is:
- Even indices are valleys:
nums[even] <= nums[even+1]
- Odd indices are peaks:
nums[odd] >= nums[odd+1]
Wrong Implementation:
# Incorrect: Swapped the conditions if (i % 2 == 0 and nums[i] < nums[i - 1]) or \ (i % 2 == 1 and nums[i] > nums[i - 1]): nums[i], nums[i - 1] = nums[i - 1], nums[i]
Solution: Remember that odd indices should be local maxima (peaks) and even indices should be local minima (valleys) when considering non-zero based indexing.
Pitfall 2: Overthinking with Sorting
Many developers initially approach this problem by sorting the array first, then trying to rearrange elements in a complex manner:
Overcomplicated Approach:
def wiggleSort(self, nums: List[int]) -> None:
sorted_nums = sorted(nums)
# Try to interleave small and large elements
for i in range(len(nums)):
if i % 2 == 0:
nums[i] = sorted_nums[i // 2]
else:
nums[i] = sorted_nums[len(nums) // 2 + i // 2]
This approach is unnecessarily complex and has O(n log n) time complexity due to sorting.
Solution: Trust the greedy approach. The single-pass algorithm with local swaps is sufficient and optimal at O(n) time complexity.
Pitfall 3: Attempting to Maintain Global Ordering
Some developers try to ensure that all elements at odd indices are globally larger than elements at even indices, which is NOT required:
Incorrect Assumption:
# Wrong: Trying to ensure all odd-index elements > all even-index elements
def wiggleSort(self, nums: List[int]) -> None:
nums.sort()
mid = len(nums) // 2
# Trying to separate into "small" and "large" groups
result = []
for i in range(mid):
result.append(nums[i])
if mid + i < len(nums):
result.append(nums[mid + i])
nums[:] = result
Solution: The wiggle pattern only requires local relationships between adjacent elements, not global ordering. An element at an odd index only needs to be >= its immediate neighbors, not all elements at even indices.
Pitfall 4: Forgetting Edge Cases with Small Arrays
Developers sometimes forget to handle arrays with only 1 or 2 elements properly:
Potential Issue:
def wiggleSort(self, nums: List[int]) -> None:
# Might cause issues if not careful with bounds
for i in range(len(nums)): # Starting from 0 instead of 1
if i % 2 == 1 and nums[i] < nums[i - 1]: # i-1 could be -1
nums[i], nums[i - 1] = nums[i - 1], nums[i]
Solution: Always start the iteration from index 1 (as in the correct solution) since we're comparing with the previous element. Arrays with length 1 are already valid wiggle arrays.
How many times is a tree node visited in a depth first search?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Donβt Miss This!