1、排序
考虑到在新的数组中,我们奇数位上的数字都要比相邻的偶数位上的数字大。因此我们可以先对数组进行排序,将前半部分较小的数字安排到偶数位上,将后半段较大的数字安排到奇数位上。值得注意的是,在此处我们可能会出现存在相邻数字相同的情况,因此我们还需要对数字的位置进行调整。考虑到若出现相同数字相邻的情况,一定是前半个区间的末尾和后半个区间的开头数字相同。为了避免出现这样的情况,我们可以先对前半个区间和后半个区间进行逆序操作,而后再组合成新数组。
class Solution {
public:
void wiggleSort(vector<int> &nums) {
vector<int> sorted{nums};
sort(sorted.begin(), sorted.end());
for (int i = 0; i < (sorted.size() + 1) / 2; ++i) {
nums[2 * i] = sorted[(sorted.size() + 1) / 2 - i - 1];
}
for (int i = 0; i < sorted.size() / 2; ++i) {
nums[2 * i + 1] = sorted[sorted.size() - i-1];
}
}
};
2、快排+虚地址
考虑到实际上我们并不需要将两个区间内的数组进行排序,实际上我们只需要获得当前数组的中位数和两个对应区间即可。为此我们可以使用快速排序来确定当前数组的中位数,而后对两个区间内的数字进行放置即可。
我们可以使用nth_element
找到当前序列中的中位数,并将中位数移动到对应位置处。而后我们对于当前数组,我们从数组的两端开始相向遍历:1、若左指针指向的元素大于中位数,则交换左右指针指向的元素并将右指针左移;2、若左指针指向的元素小于中位数,则将当前元素放置左区间的有序区间内;3、若左指针指向的元素等于中位数,则右移左指针。最终我们在最当前数组进行排序即可。而后我们直接使用虚拟地址确定最终的位置。
class Solution {
public:
void wiggleSort(vector<int>& nums) {
auto midptr = nums.begin() + nums.size() / 2;
nth_element(nums.begin(), midptr, nums.end());
int mid = *midptr;
// 3-way-partition
int i = 0, j = 0, k = nums.size() - 1;
while(j < k){
if(nums[j] > mid){
swap(nums[j], nums[k]);
--k;
}
else if(nums[j] < mid){
swap(nums[j], nums[i]);
++i;
++j;
}
else{
++j;
}
}
if(nums.size() % 2) ++midptr;
vector<int> tmp1(nums.begin(), midptr);
vector<int> tmp2(midptr, nums.end());
for(int i = 0; i < tmp1.size(); ++i){
nums[2 * i] = tmp1[tmp1.size() - 1 - i];
}
for(int i = 0; i < tmp2.size(); ++i){
nums[2 * i + 1] = tmp2[tmp2.size() - 1 - i];
}
}
};
或加上虚拟地址
class Solution {
public:
void wiggleSort(vector<int>& nums) {
int n = nums.size();
// Find a median.
auto midptr = nums.begin() + n / 2;
nth_element(nums.begin(), midptr, nums.end());
int mid = *midptr;
// Index-rewiring.
#define A(i) nums[(1+2*(i)) % (n|1)]
// 3-way-partition-to-wiggly in O(n) time with O(1) space.
int i = 0, j = 0, k = n - 1;
while (j <= k) {
if (A(j) > mid)
swap(A(i++), A(j++));
else if (A(j) < mid)
swap(A(j), A(k--));
else
j++;
}
}
};