题目:
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?
思路:
1、排序法:这是最朴素的做法,思路也很简单:1)对数组进行排序;2)从左到右奇数索引位置放大于中位数的数;3)从右到左在偶数索引位置放小于中位数的数,剩下的位置都放中位数。这种做法的时间复杂度是O(nlogn),空间复杂度是O(n)。可以通过Leetcode的所有测试数据,但是并不符合题目对时间复杂度和空间复杂度的要求,下面我们进行优化。
2、就地置换法:在排序法中,我们是独立的插入大于或者小于中位数的数,现在我们可以同时做,也就是在遍历数组的时候,如果当前的数大于中位数就将其从左往右放在奇数位置,如果小于中位数就将其从右往左放在偶数位置。这样还需要解决的一个问题是如何可以将他们互不干扰地放在正确的位置。举个例子:1,1,2,2,2,3这6个数:
我们可以按照这样一个顺序将数组索引拉开成这样:1,3,5,0,2,4,也就是我们可以按照这样一个顺序来遍历数组,同时维护一个低位指针用来代表奇数位置已经放到哪里,和一个高位指针来表示偶数位置已经放到哪里,这样在我们遍历数组的每一个元素的时候,就可以按照这样的方式将元素分别放高位还是低位。而要将数组按照这样的索引方式遍历,我们只需要一个映射即可,即(2 * i + 1) % (len | 1) (老实说,我也没看懂这个映射到底是怎么回事。。。)。
代码:
1、排序法:
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;
}
};
2、就地置换法:
class Solution {
public:
void wiggleSort(vector<int>& nums) {
nth_element(nums.begin(), nums.begin()+nums.size()/2, nums.end()); // time complexity is O(n)
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;
}
}
}
};