package com.freetymekiyan.algorithms.level.medium; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3].... * <p> * Example: * (1) Given nums = [1, 5, 1, 1, 6, 4], one possible answer is [1, 4, 1, 5, 1, 6]. * (2) Given nums = [1, 3, 2, 2, 3, 1], one possible answer is [2, 3, 1, 3, 1, 2]. * <p> * Note: * You may assume all input has valid answer. * <p> * Follow Up: * Can you do it in O(n) time and/or in-place with O(1) extra space? * <p> * Tags: Sort * Similar Problems: (M) Sort Colors, (M) Kth Largest Element in an Array, (M) Wiggle Sort */ public class WiggleSort2 { private WiggleSort2 w; /** * Quick select + Three-way partition. * Find the median with quick select. * Then put smaller ones on even indices. * Put larger ones on odd indices. * https://discuss.leetcode.com/topic/41464/step-by-step-explanation-of-index-mapping-in-java/11 */ public void wiggleSort(int[] nums) { int n = nums.length; int median = quickSelect((n + 1) / 2, nums); int left = 0; int i = 0; int right = n - 1; while (i <= right) { if (nums[newIndex(i, n)] > median) { swap(nums, newIndex(left, n), newIndex(i, n)); left++; i++; } else if (nums[newIndex(i, n)] < median) { swap(nums, newIndex(right, n), newIndex(i, n)); right--; } else { i++; } } } /** * Create an index mapping. * [0, 1, 2, 3, 4, 5] -> [1, 3, 5, 0, 2, 4] * [0, 1, 2, 3, 4, 5, 6] -> [1, 3, 5, 0, 2, 4, 6] */ private int newIndex(int i, int n) { return (2 * i + 1) % (n | 1); } /** * Return the ranking position of k in nums. */ private int quickSelect(int k, int[] nums) { int l = 0; int r = nums.length - 1; while (l < r) { int pos = l + (r - l) / 2; pos = partition(nums, l, r, pos); if (pos == k) { return nums[pos]; } else if (pos < k) { l = pos + 1; } else { r = pos - 1; } } return nums[l]; } private int partition(int[] nums, int l, int r, int pivot) { int val = nums[pivot]; swap(nums, pivot, r); // Move pivot to the end int storeIndex = l; for (int i = l; i < r; i++) { if (nums[i] < val) { // Move numbers smaller than pivot to the front swap(nums, storeIndex, i); storeIndex++; } } swap(nums, storeIndex, r); return l; } private void swap(int[] nums, int i, int j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; } @Before public void setUp() { w = new WiggleSort2(); } @Test public void testExamples() { int[] nums = {1, 2, 3, 4, 5, 6}; System.out.println(w.quickSelect((nums.length) / 2, nums)); } @After public void tearDown() { w = null; } }