Bird
Raised Fist0
Interview Prepsort-and-searchmediumGoogleAmazon

Wiggle Sort II

Choose your preparation mode4 modes available
🎯
Wiggle Sort II
mediumSORT_SEARCHGoogleAmazon

Imagine arranging a line of dancers so that their heights alternate between taller and shorter, creating a visually appealing zigzag pattern.

💡 This problem requires rearranging elements to satisfy a specific alternating order, which can be tricky because naive sorting doesn't guarantee the wiggle property. Beginners often struggle to understand how to place elements to maintain the strict inequalities required. Think of it like weaving two sorted halves of the array together so that the pattern of smaller and larger elements alternates.
📋
Problem Statement

Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3].... Input: An integer array nums. Output: Modify nums in-place to satisfy the wiggle property described.

1 ≤ nums.length ≤ 10^5-10^9 ≤ nums[i] ≤ 10^9
💡
Example
Input"[1, 5, 1, 1, 6, 4]"
Output[1, 6, 1, 5, 1, 4]

After rearrangement, the array satisfies nums[0] < nums[1] > nums[2] < nums[3] > nums[4] < nums[5].

  • All elements are equal → output is the same array
  • Array with only one element → output is the same array
  • Array with two elements → output is sorted to satisfy wiggle
  • Array with duplicates and multiple median values → correct partitioning needed
⚠️
Common Mistakes
Not using virtual indexing and placing elements directly by index

Wiggle property fails because elements are not properly interleaved

✅ Use virtual indexing to map indices to correct wiggle positions

Using median as nums[n//2] without sorting or selecting

Incorrect median leads to wrong partitioning and failed wiggle property

✅ Find median using quickselect or sort before picking median

Not handling duplicates properly in partitioning

Equal elements cluster and break strict inequalities

✅ Use three-way partitioning to separate less than, equal to, and greater than median

Modifying the array while iterating without adjusting pointers

Infinite loops or skipped elements during partitioning

✅ Adjust pointers carefully after swaps in three-way partitioning

Using extra space unnecessarily in optimal approach

Increased space complexity and possible interview penalty

✅ Implement in-place swaps with virtual indexing

🧠
Brute Force (Sorting and Rearranging with Extra Space)
💡 This approach exists to build intuition by directly sorting and then rearranging elements to meet the wiggle condition, even if it uses extra space and is not optimal. It helps beginners understand the problem by breaking it down into simpler steps.

Intuition

Sort the array, then place the smaller half of elements at even indices and the larger half at odd indices to try to satisfy the wiggle property.

Algorithm

  1. Sort the input array.
  2. Split the sorted array into two halves: smaller and larger.
  3. Place elements from the smaller half at even indices in the result array.
  4. Place elements from the larger half at odd indices in the result array.
💡 The main challenge is understanding how splitting and weaving sorted halves can create the wiggle pattern, even if it uses extra space. This approach is easy to implement and verify.
</>
Code
def wiggleSort(nums):
    n = len(nums)
    sorted_nums = sorted(nums)
    res = [0] * n
    left = (n - 1) // 2
    right = n - 1
    for i in range(n):
        if i % 2 == 0:
            res[i] = sorted_nums[left]
            left -= 1
        else:
            res[i] = sorted_nums[right]
            right -= 1
    for i in range(n):
        nums[i] = res[i]

# Driver code
if __name__ == '__main__':
    arr = [1, 5, 1, 1, 6, 4]
    wiggleSort(arr)
    print(arr)
Line Notes
n = len(nums)Calculate the length of the input array to manage indices and pointers.
sorted_nums = sorted(nums)Sort the array to organize elements for splitting into smaller and larger halves.
res = [0] * nCreate a new array to store the rearranged elements without modifying the original during processing.
left = (n - 1) // 2Initialize pointer at the end of the smaller half (middle of sorted array) to pick smaller elements from right to left.
right = n - 1Initialize pointer at the end of the larger half to pick larger elements from right to left.
for i in range(n)Copy rearranged elements back to the original array to modify in-place.
if i % 2 == 0Place smaller half elements at even indices to satisfy nums[even] < nums[odd].
res[i] = sorted_nums[left]Assign element from smaller half to current even index.
left -= 1Move pointer leftwards in smaller half to pick next smaller element.
elsePlace larger half elements at odd indices to satisfy nums[odd] > nums[even].
res[i] = sorted_nums[right]Assign element from larger half to current odd index.
right -= 1Move pointer leftwards in larger half to pick next larger element.
nums[i] = res[i]Overwrite original array with wiggle sorted elements.
import java.util.*;
public class Solution {
    public void wiggleSort(int[] nums) {
        int n = nums.length;
        int[] sorted = nums.clone();
        Arrays.sort(sorted);
        int[] res = new int[n];
        int left = (n - 1) / 2;
        int right = n - 1;
        for (int i = 0; i < n; i++) {
            if (i % 2 == 0) {
                res[i] = sorted[left--];
            } else {
                res[i] = sorted[right--];
            }
        }
        for (int i = 0; i < n; i++) {
            nums[i] = res[i];
        }
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        int[] arr = {1, 5, 1, 1, 6, 4};
        sol.wiggleSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}
Line Notes
int n = nums.length;Get the length of the input array to manage indices and pointers.
int[] sorted = nums.clone();Clone the array to avoid modifying the original during sorting.
Arrays.sort(sorted);Sort the cloned array to split into smaller and larger halves.
int[] res = new int[n];Create a new array to store rearranged elements without affecting original during processing.
int left = (n - 1) / 2;Pointer for smaller half starting from the middle element, moving leftwards.
int right = n - 1;Pointer for larger half starting from the end, moving leftwards.
for (int i = 0; i < n; i++) {Copy rearranged elements back to original array.
if (i % 2 == 0) {Place smaller half elements at even indices to maintain wiggle property.
res[i] = sorted[left--];Assign element from smaller half and decrement pointer.
} else {Place larger half elements at odd indices.
res[i] = sorted[right--];Assign element from larger half and decrement pointer.
nums[i] = res[i];Overwrite original array with wiggle sorted elements.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

void wiggleSort(vector<int>& nums) {
    int n = nums.size();
    vector<int> sorted(nums);
    sort(sorted.begin(), sorted.end());
    vector<int> res(n);
    int left = (n - 1) / 2;
    int right = n - 1;
    for (int i = 0; i < n; i++) {
        if (i % 2 == 0) {
            res[i] = sorted[left--];
        } else {
            res[i] = sorted[right--];
        }
    }
    for (int i = 0; i < n; i++) {
        nums[i] = res[i];
    }
}

int main() {
    vector<int> arr = {1, 5, 1, 1, 6, 4};
    wiggleSort(arr);
    for (int num : arr) cout << num << " ";
    cout << endl;
    return 0;
}
Line Notes
int n = nums.size();Get size of input vector to manage indices and pointers.
vector<int> sorted(nums);Copy input to a new vector for sorting without modifying original.
sort(sorted.begin(), sorted.end());Sort the copied vector to split into smaller and larger halves.
vector<int> res(n);Create result vector to store rearranged elements separately.
int left = (n - 1) / 2;Pointer for smaller half starting at middle, moving leftwards.
int right = n - 1;Pointer for larger half starting at end, moving leftwards.
for (int i = 0; i < n; i++) {Copy rearranged elements back to original vector.
if (i % 2 == 0) {Place smaller half elements at even indices to maintain wiggle property.
res[i] = sorted[left--];Assign element from smaller half and decrement pointer.
} else {Place larger half elements at odd indices.
res[i] = sorted[right--];Assign element from larger half and decrement pointer.
nums[i] = res[i];Overwrite original vector with wiggle sorted elements.
var wiggleSort = function(nums) {
    const n = nums.length;
    const sorted = [...nums].sort((a,b) => a - b);
    const res = new Array(n);
    let left = Math.floor((n - 1) / 2);
    let right = n - 1;
    for (let i = 0; i < n; i++) {
        if (i % 2 === 0) {
            res[i] = sorted[left--];
        } else {
            res[i] = sorted[right--];
        }
    }
    for (let i = 0; i < n; i++) {
        nums[i] = res[i];
    }
};

// Driver code
const arr = [1,5,1,1,6,4];
wiggleSort(arr);
console.log(arr);
Line Notes
const n = nums.length;Get length of input array to manage indices and pointers.
const sorted = [...nums].sort((a,b) => a - b);Create sorted copy of input to split into halves without modifying original.
const res = new Array(n);Create new array for rearranged elements to avoid in-place conflicts.
let left = Math.floor((n - 1) / 2);Pointer for smaller half starting at middle, moving leftwards.
let right = n - 1;Pointer for larger half starting at end, moving leftwards.
for (let i = 0; i < n; i++) {Copy rearranged elements back to original array.
if (i % 2 === 0) {Place smaller half elements at even indices to maintain wiggle property.
res[i] = sorted[left--];Assign element from smaller half and decrement pointer.
} else {Place larger half elements at odd indices.
res[i] = sorted[right--];Assign element from larger half and decrement pointer.
nums[i] = res[i];Overwrite original array with wiggle sorted elements.
⏱
Complexity
TimeO(n log n)
SpaceO(n)

Sorting takes O(n log n), and rearranging takes O(n). Extra space is used for the result array to avoid in-place conflicts.

💡 For n=100,000, sorting is the most expensive step, but this approach is straightforward and easy to implement, making it a good starting point.
Interview Verdict: Accepted but not optimal due to extra space

This approach works but uses extra space and is slower than optimal solutions, so it's good for initial understanding but not ideal for interviews.

🧠
Median Finding + Virtual Indexing + Three-Way Partitioning
💡 This approach improves on the brute force by using the median to partition the array in-place, avoiding extra space and ensuring the wiggle property. It is more complex but efficient.

Intuition

Find the median, then reorder elements so that numbers greater than the median go to odd indices and smaller ones to even indices, using a virtual indexing scheme to avoid conflicts.

Algorithm

  1. Find the median of the array using a linear-time selection algorithm.
  2. Define a virtual index mapping to rearrange indices for wiggle placement.
  3. Use three-way partitioning (Dutch National Flag) to reorder elements around the median.
  4. After partitioning, the array satisfies the wiggle property in-place.
💡 The key difficulty is understanding the virtual indexing and how it helps place elements correctly without extra space. The three-way partitioning separates elements into less than, equal to, and greater than median groups.
</>
Code
def wiggleSort(nums):
    n = len(nums)
    def nth_element(nums, n):
        import random
        def select(left, right, n_smallest):
            if left == right:
                return nums[left]
            pivot_index = random.randint(left, right)
            pivot_index = partition(left, right, pivot_index)
            if n_smallest == pivot_index:
                return nums[n_smallest]
            elif n_smallest < pivot_index:
                return select(left, pivot_index - 1, n_smallest)
            else:
                return select(pivot_index + 1, right, n_smallest)
        def partition(left, right, pivot_index):
            pivot = nums[pivot_index]
            nums[pivot_index], nums[right] = nums[right], nums[pivot_index]
            store_index = left
            for i in range(left, right):
                if nums[i] < pivot:
                    nums[store_index], nums[i] = nums[i], nums[store_index]
                    store_index += 1
            nums[right], nums[store_index] = nums[store_index], nums[right]
            return store_index
        return select(0, len(nums) - 1, n)

    median = nth_element(nums, (n - 1) // 2)

    def virtual_index(i):
        return (1 + 2 * i) % (n | 1)

    left, i, right = 0, 0, n - 1
    while i <= right:
        vi = virtual_index(i)
        if nums[vi] > median:
            vleft = virtual_index(left)
            nums[vi], nums[vleft] = nums[vleft], nums[vi]
            left += 1
            i += 1
        elif nums[vi] < median:
            vright = virtual_index(right)
            nums[vi], nums[vright] = nums[vright], nums[vi]
            right -= 1
        else:
            i += 1

# Driver code
if __name__ == '__main__':
    arr = [1, 5, 1, 1, 6, 4]
    wiggleSort(arr)
    print(arr)
Line Notes
def nth_element(nums, n):Function to find nth smallest element (median) in average linear time using quickselect.
pivot_index = random.randint(left, right)Random pivot selection to improve average case performance and avoid worst-case.
if nums[i] < pivot:Partition elements less than pivot to the left side.
median = nth_element(nums, (n - 1) // 2)Find median element to partition array around for wiggle sorting.
def virtual_index(i):Map original indices to virtual indices to place elements in wiggle order without conflicts.
return (1 + 2 * i) % (n | 1)Ensure odd indices are filled first, wrapping around to cover all indices.
while i <= right:Three-way partition loop to reorder elements around median.
if nums[vi] > median:Place elements greater than median at the front (odd indices).
nums[vi], nums[vleft] = nums[vleft], nums[vi]Swap elements to correct position according to virtual index.
elif nums[vi] < median:Place elements less than median at the end (even indices).
nums[vi], nums[vright] = nums[vright], nums[vi]Swap elements to correct position according to virtual index.
else:Elements equal to median remain in the middle section.
import java.util.*;
public class Solution {
    public void wiggleSort(int[] nums) {
        int n = nums.length;
        int median = findKthSmallest(nums, (n - 1) / 2);

        int left = 0, i = 0, right = n - 1;
        while (i <= right) {
            int vi = virtualIndex(i, n);
            if (nums[vi] > median) {
                int vleft = virtualIndex(left, n);
                swap(nums, vi, vleft);
                left++;
                i++;
            } else if (nums[vi] < median) {
                int vright = virtualIndex(right, n);
                swap(nums, vi, vright);
                right--;
            } else {
                i++;
            }
        }
    }

    private int virtualIndex(int i, int n) {
        return (1 + 2 * i) % (n | 1);
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    private int findKthSmallest(int[] nums, int k) {
        return quickSelect(nums, 0, nums.length - 1, k);
    }

    private int quickSelect(int[] nums, int left, int right, int k) {
        if (left == right) return nums[left];
        Random rand = new Random();
        int pivotIndex = left + rand.nextInt(right - left + 1);
        pivotIndex = partition(nums, left, right, pivotIndex);
        if (k == pivotIndex) return nums[k];
        else if (k < pivotIndex) return quickSelect(nums, left, pivotIndex - 1, k);
        else return quickSelect(nums, pivotIndex + 1, right, k);
    }

    private int partition(int[] nums, int left, int right, int pivotIndex) {
        int pivot = nums[pivotIndex];
        swap(nums, pivotIndex, right);
        int storeIndex = left;
        for (int i = left; i < right; i++) {
            if (nums[i] < pivot) {
                swap(nums, storeIndex, i);
                storeIndex++;
            }
        }
        swap(nums, storeIndex, right);
        return storeIndex;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        int[] arr = {1, 5, 1, 1, 6, 4};
        sol.wiggleSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}
Line Notes
int median = findKthSmallest(nums, (n - 1) / 2);Find median element using quickselect to partition array efficiently.
while (i <= right) {Three-way partition loop to reorder elements around median.
int vi = virtualIndex(i, n);Map index to virtual index for correct wiggle placement.
if (nums[vi] > median) {Place elements greater than median at front (odd indices).
swap(nums, vi, vleft);Swap elements to correct position according to virtual index.
else if (nums[vi] < median) {Place elements less than median at end (even indices).
swap(nums, vi, vright);Swap elements to correct position according to virtual index.
private int virtualIndex(int i, int n) {Virtual index mapping function to reorder indices.
private int findKthSmallest(int[] nums, int k) {Quickselect to find kth smallest element (median).
private int partition(int[] nums, int left, int right, int pivotIndex) {Partition helper for quickselect.
#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
using namespace std;

class Solution {
public:
    int virtualIndex(int i, int n) {
        return (1 + 2 * i) % (n | 1);
    }

    void swap(vector<int>& nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    int partition(vector<int>& nums, int left, int right, int pivotIndex) {
        int pivot = nums[pivotIndex];
        swap(nums, pivotIndex, right);
        int storeIndex = left;
        for (int i = left; i < right; i++) {
            if (nums[i] < pivot) {
                swap(nums, storeIndex, i);
                storeIndex++;
            }
        }
        swap(nums, storeIndex, right);
        return storeIndex;
    }

    int quickSelect(vector<int>& nums, int left, int right, int k) {
        if (left == right) return nums[left];
        random_device rd;
        mt19937 gen(rd());
        uniform_int_distribution<> dis(left, right);
        int pivotIndex = dis(gen);
        pivotIndex = partition(nums, left, right, pivotIndex);
        if (k == pivotIndex) return nums[k];
        else if (k < pivotIndex) return quickSelect(nums, left, pivotIndex - 1, k);
        else return quickSelect(nums, pivotIndex + 1, right, k);
    }

    void wiggleSort(vector<int>& nums) {
        int n = nums.size();
        int median = quickSelect(nums, 0, n - 1, (n - 1) / 2);
        int left = 0, i = 0, right = n - 1;
        while (i <= right) {
            int vi = virtualIndex(i, n);
            if (nums[vi] > median) {
                int vleft = virtualIndex(left, n);
                swap(nums, vi, vleft);
                left++;
                i++;
            } else if (nums[vi] < median) {
                int vright = virtualIndex(right, n);
                swap(nums, vi, vright);
                right--;
            } else {
                i++;
            }
        }
    }
};

int main() {
    vector<int> arr = {1, 5, 1, 1, 6, 4};
    Solution sol;
    sol.wiggleSort(arr);
    for (int num : arr) cout << num << " ";
    cout << endl;
    return 0;
}
Line Notes
int virtualIndex(int i, int n) {Virtual index mapping to reorder indices for wiggle placement.
int partition(vector<int>& nums, int left, int right, int pivotIndex) {Partition function for quickselect to find median.
int quickSelect(vector<int>& nums, int left, int right, int k) {Quickselect to find median element efficiently.
int median = quickSelect(nums, 0, n - 1, (n - 1) / 2);Find median element to partition around.
while (i <= right) {Three-way partition loop to reorder elements around median.
if (nums[vi] > median) {Place elements greater than median at front (odd indices).
swap(nums, vi, vleft);Swap elements to correct position according to virtual index.
else if (nums[vi] < median) {Place elements less than median at end (even indices).
swap(nums, vi, vright);Swap elements to correct position according to virtual index.
else {Elements equal to median stay in the middle.
var wiggleSort = function(nums) {
    const n = nums.length;
    function quickSelect(arr, left, right, k) {
        if (left === right) return arr[left];
        const pivotIndex = left + Math.floor(Math.random() * (right - left + 1));
        const pivotNewIndex = partition(arr, left, right, pivotIndex);
        if (k === pivotNewIndex) return arr[k];
        else if (k < pivotNewIndex) return quickSelect(arr, left, pivotNewIndex - 1, k);
        else return quickSelect(arr, pivotNewIndex + 1, right, k);
    }
    function partition(arr, left, right, pivotIndex) {
        const pivot = arr[pivotIndex];
        [arr[pivotIndex], arr[right]] = [arr[right], arr[pivotIndex]];
        let storeIndex = left;
        for (let i = left; i < right; i++) {
            if (arr[i] < pivot) {
                [arr[storeIndex], arr[i]] = [arr[i], arr[storeIndex]];
                storeIndex++;
            }
        }
        [arr[right], arr[storeIndex]] = [arr[storeIndex], arr[right]];
        return storeIndex;
    }
    function virtualIndex(i) {
        return (1 + 2 * i) % (n | 1);
    }
    const median = quickSelect(nums, 0, n - 1, Math.floor((n - 1) / 2));
    let left = 0, i = 0, right = n - 1;
    while (i <= right) {
        const vi = virtualIndex(i);
        if (nums[vi] > median) {
            const vleft = virtualIndex(left);
            [nums[vi], nums[vleft]] = [nums[vleft], nums[vi]];
            left++;
            i++;
        } else if (nums[vi] < median) {
            const vright = virtualIndex(right);
            [nums[vi], nums[vright]] = [nums[vright], nums[vi]];
            right--;
        } else {
            i++;
        }
    }
};

// Driver code
const arr = [1,5,1,1,6,4];
wiggleSort(arr);
console.log(arr);
Line Notes
function quickSelect(arr, left, right, k) {Quickselect to find kth smallest element (median) efficiently.
const pivotIndex = left + Math.floor(Math.random() * (right - left + 1));Random pivot selection to improve average case performance.
function partition(arr, left, right, pivotIndex) {Partition helper for quickselect to reorder elements.
function virtualIndex(i) {Virtual index mapping to reorder indices for wiggle placement.
const median = quickSelect(nums, 0, n - 1, Math.floor((n - 1) / 2));Find median element to partition around.
while (i <= right) {Three-way partition loop to reorder elements around median.
if (nums[vi] > median) {Place elements greater than median at front (odd indices).
[nums[vi], nums[vleft]] = [nums[vleft], nums[vi]];Swap elements to correct position according to virtual index.
else if (nums[vi] < median) {Place elements less than median at end (even indices).
[nums[vi], nums[vright]] = [nums[vright], nums[vi]];Swap elements to correct position according to virtual index.
else {Elements equal to median stay in the middle.
⏱
Complexity
TimeO(n) average
SpaceO(1)

Median finding via quickselect is O(n) average, and three-way partitioning is O(n). No extra arrays used, so space is constant.

💡 For n=100,000, this approach is efficient and uses constant extra space, making it ideal for large inputs and interviews.
Interview Verdict: Accepted and optimal in average case

This is the preferred approach in interviews due to its efficiency and in-place operation, though it is more complex to implement.

🧠
Sorting + In-place Rearrangement Using Virtual Indexing
💡 This approach combines sorting with in-place rearrangement using virtual indexing to avoid extra space, but it relies on sorting first. It is simpler than median finding but less optimal.

Intuition

Sort the array, then use virtual indexing to place elements in wiggle order by swapping in-place without extra arrays.

Algorithm

  1. Sort the array.
  2. Define a virtual index mapping to reorder indices.
  3. Use three pointers to swap elements in-place according to their relation to the median.
  4. After rearrangement, the array satisfies the wiggle property.
💡 The challenge is understanding how virtual indexing helps reorder elements in-place after sorting, enabling the wiggle pattern without extra space.
</>
Code
def wiggleSort(nums):
    n = len(nums)
    nums.sort()
    median = nums[(n - 1) // 2]

    def virtual_index(i):
        return (1 + 2 * i) % (n | 1)

    left, i, right = 0, 0, n - 1
    while i <= right:
        vi = virtual_index(i)
        if nums[vi] > median:
            vleft = virtual_index(left)
            nums[vi], nums[vleft] = nums[vleft], nums[vi]
            left += 1
            i += 1
        elif nums[vi] < median:
            vright = virtual_index(right)
            nums[vi], nums[vright] = nums[vright], nums[vi]
            right -= 1
        else:
            i += 1

# Driver code
if __name__ == '__main__':
    arr = [1, 5, 1, 1, 6, 4]
    wiggleSort(arr)
    print(arr)
Line Notes
nums.sort()Sort the array to prepare for rearrangement and find median easily.
median = nums[(n - 1) // 2]Median is middle element after sorting, used to partition elements.
def virtual_index(i):Virtual index mapping to place elements in wiggle order without extra space.
while i <= right:Three-way partition loop to reorder elements around median in-place.
if nums[vi] > median:Place elements greater than median at front (odd indices).
nums[vi], nums[vleft] = nums[vleft], nums[vi]Swap elements to correct position according to virtual index.
elif nums[vi] < median:Place elements less than median at end (even indices).
nums[vi], nums[vright] = nums[vright], nums[vi]Swap elements to correct position according to virtual index.
else:Elements equal to median stay in the middle.
import java.util.*;
public class Solution {
    public void wiggleSort(int[] nums) {
        int n = nums.length;
        Arrays.sort(nums);
        int median = nums[(n - 1) / 2];

        int left = 0, i = 0, right = n - 1;
        while (i <= right) {
            int vi = virtualIndex(i, n);
            if (nums[vi] > median) {
                int vleft = virtualIndex(left, n);
                swap(nums, vi, vleft);
                left++;
                i++;
            } else if (nums[vi] < median) {
                int vright = virtualIndex(right, n);
                swap(nums, vi, vright);
                right--;
            } else {
                i++;
            }
        }
    }

    private int virtualIndex(int i, int n) {
        return (1 + 2 * i) % (n | 1);
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        int[] arr = {1, 5, 1, 1, 6, 4};
        sol.wiggleSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}
Line Notes
Arrays.sort(nums);Sort the array before rearrangement to find median and prepare for partitioning.
int median = nums[(n - 1) / 2];Median element after sorting used to partition elements.
while (i <= right) {Three-way partition loop to reorder elements around median in-place.
int vi = virtualIndex(i, n);Virtual index mapping for wiggle placement to avoid conflicts.
if (nums[vi] > median) {Place elements greater than median at front (odd indices).
swap(nums, vi, vleft);Swap elements to correct position according to virtual index.
else if (nums[vi] < median) {Place elements less than median at end (even indices).
swap(nums, vi, vright);Swap elements to correct position according to virtual index.
else {Elements equal to median stay in the middle.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class Solution {
public:
    int virtualIndex(int i, int n) {
        return (1 + 2 * i) % (n | 1);
    }

    void swap(vector<int>& nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    void wiggleSort(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        int median = nums[(n - 1) / 2];
        int left = 0, i = 0, right = n - 1;
        while (i <= right) {
            int vi = virtualIndex(i, n);
            if (nums[vi] > median) {
                int vleft = virtualIndex(left, n);
                swap(nums, vi, vleft);
                left++;
                i++;
            } else if (nums[vi] < median) {
                int vright = virtualIndex(right, n);
                swap(nums, vi, vright);
                right--;
            } else {
                i++;
            }
        }
    }
};

int main() {
    vector<int> arr = {1, 5, 1, 1, 6, 4};
    Solution sol;
    sol.wiggleSort(arr);
    for (int num : arr) cout << num << " ";
    cout << endl;
    return 0;
}
Line Notes
sort(nums.begin(), nums.end());Sort the input vector to find median and prepare for partitioning.
int median = nums[(n - 1) / 2];Median element after sorting used to partition elements.
while (i <= right) {Three-way partition loop to reorder elements around median in-place.
int vi = virtualIndex(i, n);Virtual index mapping for wiggle placement to avoid conflicts.
if (nums[vi] > median) {Place elements greater than median at front (odd indices).
swap(nums, vi, vleft);Swap elements to correct position according to virtual index.
else if (nums[vi] < median) {Place elements less than median at end (even indices).
swap(nums, vi, vright);Swap elements to correct position according to virtual index.
else {Elements equal to median stay in the middle.
var wiggleSort = function(nums) {
    const n = nums.length;
    nums.sort((a,b) => a - b);
    const median = nums[Math.floor((n - 1) / 2)];
    function virtualIndex(i) {
        return (1 + 2 * i) % (n | 1);
    }
    let left = 0, i = 0, right = n - 1;
    while (i <= right) {
        const vi = virtualIndex(i);
        if (nums[vi] > median) {
            const vleft = virtualIndex(left);
            [nums[vi], nums[vleft]] = [nums[vleft], nums[vi]];
            left++;
            i++;
        } else if (nums[vi] < median) {
            const vright = virtualIndex(right);
            [nums[vi], nums[vright]] = [nums[vright], nums[vi]];
            right--;
        } else {
            i++;
        }
    }
};

// Driver code
const arr = [1,5,1,1,6,4];
wiggleSort(arr);
console.log(arr);
Line Notes
nums.sort((a,b) => a - b);Sort the array before rearrangement to find median and prepare for partitioning.
const median = nums[Math.floor((n - 1) / 2)];Median element after sorting used to partition elements.
function virtualIndex(i) {Virtual index mapping for wiggle placement to avoid conflicts.
while (i <= right) {Three-way partition loop to reorder elements around median in-place.
if (nums[vi] > median) {Place elements greater than median at front (odd indices).
[nums[vi], nums[vleft]] = [nums[vleft], nums[vi]];Swap elements to correct position according to virtual index.
else if (nums[vi] < median) {Place elements less than median at end (even indices).
[nums[vi], nums[vright]] = [nums[vright], nums[vi]];Swap elements to correct position according to virtual index.
else {Elements equal to median stay in the middle.
⏱
Complexity
TimeO(n log n)
SpaceO(1)

Sorting takes O(n log n), rearrangement is O(n) in-place with constant extra space.

💡 This approach is easier to implement than median finding but slower due to sorting. It uses no extra space, making it a good fallback.
Interview Verdict: Accepted, simpler but less optimal than median finding

Good fallback if median finding is too complex; still uses no extra space but slower.

📊
All Approaches - One-Glance Tradeoffs
💡 The median finding + virtual indexing approach is best for interviews; brute force is good for initial understanding.
ApproachTimeSpaceStack RiskReconstructUse In Interview
1. Brute ForceO(n log n)O(n)NoN/AMention only - never code
2. Median Finding + Virtual IndexingO(n) averageO(1)Low (due to recursion in quickselect)N/ACode this for optimal solution
3. Sorting + In-place RearrangementO(n log n)O(1)NoN/AGood fallback if median finding is complex
💼
Interview Strategy
💡 Use this guide to understand the problem deeply, practice coding all approaches, and prepare to explain tradeoffs clearly in interviews.

How to Present

Step 1: Clarify the wiggle property and constraints.Step 2: Present the brute force approach using sorting and extra space.Step 3: Discuss the optimal in-place approach using median finding and virtual indexing.Step 4: Optionally mention the sorting + in-place rearrangement as a simpler alternative.Step 5: Code the chosen approach and test with edge cases.

Time Allocation

Clarify: 2min → Approach: 5min → Code: 15min → Test: 5min. Total ~27min

What the Interviewer Tests

Ability to handle complex indexing, in-place rearrangement, median finding, and understanding of partitioning techniques.

Common Follow-ups

  • What if duplicates are very frequent? → The median partitioning handles duplicates gracefully.
  • Can you do this without sorting? → Yes, using quickselect to find median and three-way partitioning.
💡 These follow-ups test understanding of median selection and handling duplicates, common in real interviews.
🔍
Pattern Recognition

When to Use

1) Need to reorder array elements to satisfy alternating inequalities, 2) Median or partitioning is involved, 3) In-place rearrangement is required, 4) Handling duplicates carefully.

Signature Phrases

nums[0] < nums[1] > nums[2] < nums[3]rearrange in-placemedian partition

NOT This Pattern When

Problems that only require sorting or simple two-pointer techniques without median or partitioning.

Similar Problems

Wiggle Sort I - simpler alternating order without strict inequalitiesSort Colors - uses three-way partitioningDutch National Flag - partitioning array into three groups

Practice

(1/5)
1. Consider the following Python code implementing the optimal approach for relative sorting. Given arr1 = [3,1,2,2] and arr2 = [2,3], what is the final returned list?
easy
A. [2, 3, 1, 2]
B. [3, 2, 2, 1]
C. [2, 2, 3, 1]
D. [1, 2, 2, 3]

Solution

  1. Step 1: Build frequency array for arr1

    arr1 = [3,1,2,2], max_val=3, freq = [0,1,2,1] (indices 0 to 3)
  2. Step 2: Construct result using arr2 order then leftovers

    Output elements for arr2: 2 appears twice -> [2,2], 3 appears once -> [2,2,3]. Leftover is 1 once -> [2,2,3,1]
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Frequency counts and order match output [OK]
Hint: Trace frequency array and output order carefully [OK]
Common Mistakes:
  • Appending leftover elements before arr2 order
  • Miscounting frequencies or off-by-one in freq array
  • Mixing order of arr2 elements in output
2. What is the time complexity of the optimal approach that sorts the array and scans adjacent pairs to find the minimum absolute difference?
medium
A. O(n) because scanning adjacent pairs is linear
B. O(n²) because every pair is compared
C. O(n log n) due to sorting, scanning is O(n)
D. O(log n) because sorting uses divide and conquer

Solution

  1. Step 1: Identify sorting cost

    Sorting an array of size n takes O(n log n) time.
  2. Step 2: Identify scanning cost

    Scanning adjacent pairs is a single pass O(n).
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Sorting dominates total time -> O(n log n) [OK]
Hint: Sorting dominates; scanning is linear [OK]
Common Mistakes:
  • Confusing scanning cost as sorting cost
  • Assuming O(n) overall without sorting
3. The following code attempts to find the minimum absolute difference but contains a subtle bug. Which line causes incorrect results?
def minimum_abs_difference(arr):
    min_diff = float('inf')
    for i in range(len(arr) - 1):
        diff = arr[i + 1] - arr[i]
        if diff < min_diff:
            min_diff = diff
    arr.sort()
    return min_diff
medium
A. Line 2: initializing min_diff before sorting
B. Line 6: sorting after scanning pairs
C. Line 7: returning min_diff without recomputing
D. Line 3: iterating without sorting first

Solution

  1. Step 1: Identify order of operations

    The array is scanned for differences before sorting, so adjacent pairs are not guaranteed to be closest.
  2. Step 2: Pinpoint the bug

    Line 3 iterates over unsorted array, causing incorrect min_diff.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Sorting must happen before scanning adjacent pairs [OK]
Hint: Must sort before scanning adjacent pairs [OK]
Common Mistakes:
  • Sorting after scanning
  • Not updating min_diff correctly
4. What is the time complexity of the optimal bucket sort based solution for finding the minimum time difference among n time points, where each time is in "HH:MM" format?
medium
A. O(n + 1440) because we mark seen times and scan a fixed-size array
B. O(n log n) due to sorting the input times
C. O(n^2) because we compare all pairs of times
D. O(1440^2) since we scan all possible time pairs in the fixed range

Solution

  1. Step 1: Mark times in a fixed-size boolean array

    Marking each of the n times in a 1440-length array is O(n).
  2. Step 2: Scan the fixed 1440-length array once

    Scanning the array to find minimum difference is O(1440), which is constant and independent of n.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Overall complexity is O(n + 1440) -> O(n) for large n [OK]
Hint: Fixed 1440 array makes scanning O(1) relative to n [OK]
Common Mistakes:
  • Assuming sorting is needed -> O(n log n)
  • Confusing nested loops -> O(n^2)
  • Thinking scanning pairs in fixed array is quadratic
5. The following code attempts to find the minimum time difference between time points. Which line contains a subtle bug that causes incorrect results on inputs with duplicate times?
def findMinDifference(timePoints):
    seen = [False] * 1440
    for t in timePoints:
        h, m = map(int, t.split(':'))
        total = h * 60 + m
        seen[total] = True
    prev = -1
    first = -1
    min_diff = 1440
    for i in range(1440):
        if seen[i]:
            if prev != -1:
                min_diff = min(min_diff, i - prev)
            else:
                first = i
            prev = i
    min_diff = min(min_diff, 1440 - prev + first)
    return min_diff
medium
A. Line updating min_diff inside the loop
B. Line initializing prev = -1
C. Line that sets seen[total] = True without checking duplicates
D. Line computing min_diff with circular difference after the loop

Solution

  1. Step 1: Check duplicate detection logic

    The code sets seen[total] = True but does not check if seen[total] was already True, missing duplicates.
  2. Step 2: Consequence of missing duplicate check

    Without detecting duplicates, the function cannot return 0 early, causing wrong minimum difference on duplicate inputs.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Adding a duplicate check before setting seen fixes the bug [OK]
Hint: Check for duplicate times before marking seen [OK]
Common Mistakes:
  • Ignoring duplicates leads to wrong min difference
  • Misplaced min_diff update
  • Forgetting circular difference check
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard