1,题目要求
Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3]…
Example 1:
Input: nums = [1, 5, 1, 1, 6, 4]
Output: One possible answer is [1, 4, 1, 5, 1, 6].
Example 2:
Input: nums = [1, 3, 2, 2, 3, 1]
Output: 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?
给定一个未排序的数组nums,重新排序,使得nums [0] <nums [1]> nums [2] <nums [3] …
2,题目思路
对于这道题,要求将一个数组按照“波浪”的值的变化来重新排序。
所谓的波浪形,就是元素的值是按照小、大、小、大。。。这种顺序进行排序的。
这里,直接引用discussion中的一位大佬的方法,在对其思考了很长时间之后,发现是非常的巧妙。
首先,我们使用nth_element找到当前数组中,值位于中间的元素,并将其放在合适的位置——也就是当前数组的中间位置,此时,当前数组的前半部分的数字都是比中间值小的, 后半部分的元素的值都是比中间值大的。
此时,我们获得了数组的中间值。
接着,我们定义一个宏:
#define A(i) nums[(1+2*(i)) % (n|1)]
这个宏所提供的,是一种映射功能:
Accessing A(0) actually accesses nums[1].
Accessing A(1) actually accesses nums[3].
Accessing A(2) actually accesses nums[5].
Accessing A(3) actually accesses nums[7].
Accessing A(4) actually accesses nums[9].
Accessing A(5) actually accesses nums[0].
Accessing A(6) actually accesses nums[2].
Accessing A(7) actually accesses nums[4].
Accessing A(8) actually accesses nums[6].
Accessing A(9) actually accesses nums[8].
之所以要这样映射,暂且不说。
之后,我们进行一种三分划分:
对于一个数组而言,当我们按照mid(中间值)进行三分划分时,会使得比mid大的都排到了mid之前——也就是数组的前半部分,而比mid小的,都排到了后半部分了。
回到这一题中,当我们利用A(i)进行映射排序后,我们就可以发现,A(i)的虚拟映射使得值比mid大的排在虚拟数组的0~mid的部分的元素,实际在nums上是被排到了1、3、5、7。。。等位置,而较小的元素,则是在nums中被排到了0,2,4,6。。。等位置。
这时,我们再观察,就发现整合数组就是按照一种小、大、小、大的顺序进行排序的了。
3,代码实现
static auto speedup = [](){
ios::sync_with_stdio(false);
cin.tie(nullptr);
return nullptr;
}();
class Solution {
public:
void wiggleSort(vector<int>& nums) {
int n = nums.size();
//首先,进行部分排序
//找到当前数组中的中间值
auto midPtr = nums.begin()+n/2;
nth_element(nums.begin(), midPtr, nums.end());
int mid = *midPtr;
//定义映射规则
#define A(i) nums[(1+2*(i)) % (n|1)]
//三分划分,将比mid大的移到数组A左边
//将比mid的元素移到数组A右边
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--)); //注意,此时j没变
else
j++;
}
return;
}
};