324. Wiggle Sort II

来源:互联网 发布:网络推广服务商 编辑:程序博客网 时间:2024/05/16 16:17

Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3]....

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].

Note:
You may assume all input has valid answer.

Follow Up:

Can you do it in O(n) time and/or in-place with O(1) extra space?

Solution:

solve it in O(n) space is easy, we could first find the median of the nums, and partition the array by smaller | median | larger, it takes O(1) time,

we want to put median numbers in the smallest odd positions and largest even positions to separate them as far as possible to avoid nums[i] == nums[i + 1];

for the index 

0 1 2 3 4 5 6 they will end to be

1 3 5 0 2 4 6

How to get this?  from https://discuss.leetcode.com/topic/41464/step-by-step-explanation-of-index-mapping-in-java/14

This is to explain why mapped index formula is (1 + 2*index) % (n | 1)

Notice that by placing the median in it's place in the array we divided the array in 3 chunks: all numbers less than median are in one side, all numbers larger than median are on the other side, median is in the dead center of the array.

We want to place any a group of numbers (larger than median) in odd slots, and another group of numbers (smaller than median) in even slots. So all numbers on left of the median < n / 2 should be in odd slots, all numbers on right of the median > n / 2 should go into even slots (remember that median is its correct place at n / 2)

PS: I'm ignoring the discussion of odd/even array length for simplicity.

So let's think about the first group in the odd slots, all numbers is the left side of the array should go into these odd slots. What's the formula for it? Naturally it would be:
(1 + 2 x index) % n

All these indexes are less than n / 2 so multiplying by 2 and add 1 (to make them go to odd place) and then mod by n will always guarantee that they are less than n.

Original Index => Mapped Index
0 => (1 + 2 x 0) % 6 = 1 % 6 = 1
1 => (1 + 2 x 1) % 6 = 3 % 6 = 3
2 => (1 + 2 x 2) % 6 = 5 % 6 = 5

These are what's less than median, if we continue this with indexes 3, 4, 5 we will cycle again:
3 => (1 + 2 x 3) % 6 = 7 % 6 = 1
4 => (1 + 2 x 4) % 6 = 9 % 6 = 3
5 => (1 + 2 x 5) % 6 = 11 % 6 = 5

and we don't want that, so for indexes larger than n/2 we want them to be even, (n|1) does that exactly. What n|1 does it that it gets the next odd number to n if it was even
if n = 6 for example 110 | 1 = 111 = 7
if n = 7 for example 111 | 1 = 111 = 7

and this is what we want, instead of cycling the odd numbers again we want them to be even, and odd % odd number is even so updating the formula to :
(1 + 2*index) % (n | 1)

Then we have:
3 => (1 + 2 x 3) % 7 = 7 % 7 = 0
4 => (1 + 2 x 4) % 7 = 9 % 7 = 2
5 => (1 + 2 x 5) % 7 = 11 % 7 = 4

And we want to finish the conversion while the partition, so we compute each mapped index in the partition method.

Code:

public class Solution {    public void wiggleSort(int[] nums) {        int mid = findKth(nums,(nums.length - 1)/ 2,0,nums.length-1);        partition(nums,mid);    }        public int mapIndex(int len, int i){        return (2*i + 1) % (len | 1);    }        public void partition(int[] nums, int val){        int len = nums.length;        int l = 0;        int r = nums.length - 1;        int i = 0;        while(i <= r){            int curIndex = mapIndex(len,i);            if(nums[curIndex] > val){                swap(nums,curIndex,mapIndex(len,l));                i++;l++;            } else if(nums[curIndex] == val){                i++;            } else {                swap(nums,curIndex,mapIndex(len,r));                r--;            }        }    }        public void swap(int[] nums, int i, int j){        int temp = nums[i];        nums[i] = nums[j];        nums[j] = temp;    }        public int findKth(int[] nums, int k, int start, int end){        int index= partition(nums,start,end);        if(index == k) return nums[k];        else if(index > k) return findKth(nums,k,start,index - 1);        else return findKth(nums,k,index + 1, end);    }        public int partition(int[] nums, int start, int end){        if(start == end) return start;        int p = nums[start];        while(start < end){            while(start < end && nums[end] > p){                end--;            }            nums[start] = nums[end];            while(start < end && nums[start] <= p){                start++;            }            nums[end] = nums[start];        }        nums[start] = p;        return start;    }}






0 0