leetcode324~Wiggle Sort II

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?

与上题不同:会有相等的元素,而且相邻元素不能等于。
该题的最优解法是O(n)的时间复杂度,O(1)的额外空间。下面先给出正常解法,然后再对最优解法进行说明。

第一种解法也是有坑的,在对新数组进行添加元素时,要从原数组的中间位置和最后位置分别加进来,如果采用从两段的头部分别添加或者都从尾部添加时,有些case是无法通过的。(考虑重复的情况)

//排序之后,从mid的元素和最后一个元素开始向新的数组tmp里面存放
    public void wiggleSort2(int[] nums) {
        Arrays.sort(nums);
        int[] tmp = new int[nums.length];
        int mid = (nums.length-1)/2;
        int index = 0;
        //数组长度为奇数时,最后只添加一个数
        for(int i=0;i<=mid;i++) {
            tmp[index] = nums[mid-i];
            if(index+1<nums.length) {
                tmp[index+1] = nums[nums.length-1-i];
            }
            index = index+2;
        }
        System.arraycopy(tmp, 0, nums, 0, tmp.length);
    }

第二种解法是用了中位数。下面引用别人的解释,very good~
在此只做简单的说明,便于读者更快的理解。
思想:先找到数组的中位数,然后基于快排的思想,利用newIndex()函数进行位置映射,将大于中位数的元素放在第一个奇数的槽位上,小于中位数的元素放在最近的偶数槽位上,其他的槽位则使用中位数进行填补。
建议:看下面解释还不清楚的话,可以结合程序看看,若还有不明白,欢迎提问~
we have learned that , to get wiggle sort, you want to put the number in the following way such that
(1) elements smaller than the ‘median’ are put into the last even slots
(2) elements larger than the ‘median’ are put into the first odd slots
(3) the medians are put into the remaining slots.
M - Median, S-Small, L-Large. In this example, we want to put {13, 6, 5} in index 1,3,5 and {5,4,2} in index {0,2,4}
The index mapping, (1 + 2*index) % (n | 1) combined with ‘Color sort’, will do the job.
Mapped_idx[Left] denotes the position where the next smaller-than median element will be inserted.
Mapped_idx[Right] denotes the position where the next larger-than median element will be inserted.

Step 1:
Original idx: 0 1 2 3 4 5
Mapped idx: 1 3 5 0 2 4
Array: 13 6 5 5 4 2
Left
i
Right
nums[Mapped_idx[i]] = nums[1] = 6 > 5, so it is ok to put 6 in the first odd index 1. We increment i and left.

Step 2:
Original idx: 0 1 2 3 4 5
Mapped idx: 1 3 5 0 2 4
Array: 13 6 5 5 4 2
Left
i
Right
nums[3] = 5 = 5, so it is ok to put 6 in the index 3. We increment i.

Step 3:
Original idx: 0 1 2 3 4 5
Mapped idx: 1 3 5 0 2 4
Array: 13 6 5 5 4 2
Left
i
Right
nums[5] = 2 < 5, so we want to put it to the last even index 4 (pointed by Right). So, we swap nums[Mapped_idx[i]] with nums[Mapped_idx[Right]], i.e. nums[5] with nums[4], and decrement Right.

Step 4:
Original idx: 0 1 2 3 4 5
Mapped idx: 1 3 5 0 2 4
Array: 13 6 5 5 2 4
Left
i
Right
nums[5] = 4 < 5, so we want to put it to the second last even index 2. So, we swap nums[5] with nums[2], and decrement Right.

Step 5:
Original idx: 0 1 2 3 4 5
Mapped idx: 1 3 5 0 2 4
Array: 13 6 4 5 2 5
Left
i
Right
nums[5] = 5 < 5, it is ok to put it there, we increment i.

Step 6:
Original idx: 0 1 2 3 4 5
Mapped idx: 1 3 5 0 2 4
Array: 13 6 4 5 2 5
Left
i
Right
nums[0] = 13 > 5, so, we want to put it to the next odd index which is 3 (pointed by ‘Left’). So, we swap nums[0] with nums[3], and increment ‘Left’ and ‘i’.

Step Final:
Original idx: 0 1 2 3 4 5
Mapped idx: 1 3 5 0 2 4
Array: 5 6 4 13 2 5
Left
i
Right
i > Right, we get the final wiggle array 5 6 4 13 2 5 !

代码如下:

public void wiggleSort(int[] nums) {
        //找到中位数
        int median = findKthLargest(nums, (nums.length+1)/2);
        int n = nums.length;
        int left=0,i=0,right=n-1;
        while(i<=right) {
            if(nums[newIndex(i,n)]>median) {
                swap(nums,newIndex(left++,n),newIndex(i++,n));
            } else if(nums[newIndex(i,n)]<median) {
                swap(nums,newIndex(right--,n),newIndex(i,n));
            } else {
                i++;
            }
        }
    }
    //长度为n,找到index对应的位置
    private int newIndex(int index, int n) {
        return (1 + 2*index) % (n | 1);
    }

    public int findKthLargest(int[] a,int k) {
        int n = a.length;
        int p = quickselect(a,0,n-1,n-k+1); 
        return a[p];
    }
    //求第k个小的元素
    //基于快速排序的思想,比x小的放在左边,大的放在右边
    private int quickselect(int[] a, int low, int high, int k) {
        int i=low,j=high,x=a[high];
        while(i<j) {
            if(a[i++]>x) {
                swap(a,--i,--j); //交换之后,需要对i--,要再次对交换后的元素进行比较
            }
        }
        //此时小的元素已经被交换到左边
        //交换最高位置的元素,便于下次的选取
        swap(a,i,high);
        //从low->x的元素个数
        int m = i-low+1;
        if(m==k) {
            return i;
        } else if(m>k) { //选取的x值过大
            return quickselect(a, low, i-1, k);
        } else { //选取的x值过小
            return quickselect(a, i+1, high, k-m);
        }
    }
    //交换元素
    private void swap(int[] a, int i, int j) {
        int tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值