题目链接:https://leetcode.com/problems/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?
思路:一种比较naive的做法是先对数组进行排序, 然后从左往右奇数索引位置放大于中位数的数, 然后从右往左在偶数索引位置放小于中位数的数, 剩下的位置都放中位数. 其时间复杂度为O(nlog(n)), 空间复杂度为O(n).
代码如下:
class Solution {
public:
void wiggleSort(vector<int>& nums) {
if(nums.size()<=1) return;
sort(nums.begin(), nums.end());
int len = nums.size(), k = 1, high = (len%2)?len-1:len-2, mid=nums[len/2];
vector<int> ans(len, mid);
for(int i = len-1; i >=0 && nums[i]>mid; i--, k+=2)
ans[k] = nums[i];
for(int i = 0; i < len && nums[i]<mid; i++, high-=2)
ans[high] = nums[i];
nums = ans;
}
};
显然上面的方法并符合要求, 但却是理解这题的一个先导. 接下来就可以对其进行优化.
之前我们是独立的插入大于或者小于中位数的数, 现在我们可以同时做, 也就是, 遍历数组的时候如果当前的数大于中位数就将其从左往右放奇数位置, 如果小于中位数就将其从右往左放在偶数位置. 这样还需要解决的一个问题是如何可以互不干扰的放到正确位置. 举个栗子: 1, 1, 2, 2, 2, 3这六个数:
我们可以按照这样一个顺序将数组索引拉开成这样: 1, 3, 5, 0, 2, 4, 也就是我们可以按照这样一个顺序来遍历数组, 同时维护一个低位指针用来代表奇数位置已经放到哪里, 和一个高位指针来表示偶数位置已经放到哪里, 这样在我们遍历数组的每一个元素的时候就可以按照这样的方式将元素分别放高位还是低位. 而要将数组按照这样索引的方式遍历我们只需要一个映射即可, 即(2*i+1)%(len|1).
代码如下:
class Solution {
public:
void wiggleSort(vector<int>& nums) {
nth_element(nums.begin(), nums.begin()+nums.size()/2, nums.end());
int len=nums.size(), low=0, high=len-1, mid =nums[len/2], i=0;
auto index = [=](int pos){ return (1+pos*2)%(len|1); };
while(i <= high)
{
if(nums[index(i)] > mid) swap(nums[index(i++)], nums[index(low++)]);
else if(nums[index(i)]<mid) swap(nums[index(i)],nums[index(high--)]);
else i++;
}
}
};
参考:https://leetcode.com/discuss/77133/o-n-o-1-after-median-virtual-indexing