Wiggle Sort II
Given an integer array nums, reorder it in place so that nums0 < nums1 > nums2 < nums3 ... — every element strictly alternates between smaller and greater than its neighbors. You may assume a valid wiggle arrangement always exists for the input, and any valid arrangement is accepted. Find the median in O(n) average time with quickselect , then partition the array into values above, equal to, and below it, placing the larger half at alternating positions.
Constraints
- 1 ≤ nums.length ≤ 5 × 104
- 0 ≤ numsi ≤ 5000
- It is guaranteed that there is a valid answer for the given input
Example
nums = [1, 5, 1, 1, 6, 4][1, 5, 1, 4, 1, 6]Explanation Multiple valid wiggle arrangements exist for this input; this is the one produced by finding the median (1) with quickselect and partitioning around it. Check it alternates: 1 < 5 > 1 < 4 > 1 < 6.
In plain terms
- Median
- The middle value of a sorted array — half the values are less than or equal to it, half are greater than or equal to it.
Find the median, then partition around it into alternating positions
Quickselect found the median (1) in O(n) average time. Now partition the array around it.
What happens in this step
nums = [1, 1, 2, 2] (simplified 4-element example) sortedCopy = [1, 1, 2, 2] median = quickSelect(sortedCopy, k = floor((4-1)/2) = 1) = 1 n=4 is even, so n | 1 = 5 and mapIndex(i) = (2i + 1) % 5 walks alternating positions from both ends inward: mapIndex(0)=1, mapIndex(1)=3, mapIndex(2)=0, mapIndex(3)=2. left=0, mid=0, right=3 — ready to partition nums around the median using that index mapping.
Steps to visualize
- Run quickselect on a copy of the array to find its median value in O(n) average time.
- Walk through the original array once, comparing each value to the median.
- Values greater than the median move toward the even (0-indexed) positions from the outside in.
- Values less than the median move toward the odd positions from the outside in.
- Values equal to the median stay put — the alternating placement guarantees no two equal values end up adjacent.
Walk through the code
Same walkthrough, now with the code. Press Next to move one step and watch which lines run.
Quickselect found the median (1) in O(n) average time. Now partition the array around it.
What happens in this step
nums = [1, 1, 2, 2] (simplified 4-element example) sortedCopy = [1, 1, 2, 2] median = quickSelect(sortedCopy, k = floor((4-1)/2) = 1) = 1 n=4 is even, so n | 1 = 5 and mapIndex(i) = (2i + 1) % 5 walks alternating positions from both ends inward: mapIndex(0)=1, mapIndex(1)=3, mapIndex(2)=0, mapIndex(3)=2. left=0, mid=0, right=3 — ready to partition nums around the median using that index mapping.
Solution
function wiggleSort(nums) {
const n = nums.length;
function partition(arr, lo, hi) {
const pivot = arr[hi];
let i = lo;
for (let j = lo; j < hi; j++) {
if (arr[j] < pivot) {
[arr[i], arr[j]] = [arr[j], arr[i]];
i++;
}
}
[arr[i], arr[hi]] = [arr[hi], arr[i]];
return i;
}
function quickSelect(arr, k) {
let lo = 0;
let hi = arr.length - 1;
while (lo < hi) {
const pivotIndex = partition(arr, lo, hi);
if (pivotIndex === k) break;
if (pivotIndex < k) lo = pivotIndex + 1;
else hi = pivotIndex - 1;
}
return arr[k];
}
const sortedCopy = nums.slice();
const median = quickSelect(sortedCopy, Math.floor((n - 1) / 2));
const mapIndex = (i) => (2 * i + 1) % (n | 1);
let left = 0;
let mid = 0;
let right = n - 1;
while (mid <= right) {
const midValue = nums[mapIndex(mid)];
if (midValue > median) {
const li = mapIndex(left);
const mi = mapIndex(mid);
[nums[li], nums[mi]] = [nums[mi], nums[li]];
left++;
mid++;
} else if (midValue < median) {
const mi = mapIndex(mid);
const ri = mapIndex(right);
[nums[mi], nums[ri]] = [nums[ri], nums[mi]];
right--;
} else {
mid++;
}
}
return nums;
}- Time
- O(n) average, O(n²) worst case, for the median quickselect plus an O(n) partition
- Space
- O(n) for the sorted copy used to find the median
Test cases
| Input | Expected | Covers |
|---|---|---|
nums = [1, 5, 1, 1, 6, 4] | [1, 5, 1, 4, 1, 6] | example from the docstring, with duplicate values |
nums = [1, 3, 2, 2, 3, 1] | [2, 3, 1, 3, 1, 2] | even length with several repeated values around the median |
nums = [5] | [5] | smallest valid input, a single element trivially satisfies the wiggle property |
nums = [2, 1] | [1, 2] | boundary case, exactly two elements |
nums = [4, 2, 3] | [3, 4, 2] | odd length array, a true single median value |
nums = [1, 1, 2, 2] | [1, 2, 1, 2] | even length with only two distinct values, the classic adjacent-duplicate trap |