324. 摆动排序 II
给定一个无序的数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]… 的顺序。
示例 1:
输入: nums = [1, 5, 1, 1, 6, 4]
输出: 一个可能的答案是 [1, 4, 1, 5, 1, 6]
示例 2:
输入: nums = [1, 3, 2, 2, 3, 1]
输出: 一个可能的答案是 [2, 3, 1, 3, 1, 2]
说明:
你可以假设所有输入都会得到有效的结果。
进阶:
你能用 O(n) 时间复杂度和 / 或原地 O(1) 额外空间来实现吗?
解题
Sort+临时数组
时间复杂度O(nlogn),空间复杂度O(n)
将数组排序;
分为前半部和后半部;
从后往前穿插给新数组;
再赋值给原始数组;
class Solution {
public:
void wiggleSort(vector<int>& nums) {
sort(nums.begin(),nums.end());
int n=nums.size();
vector<int> res;
res.resize(n);
int a=(n-1)/2;
int b=n-1;
int i=0;
while(a>=0&&b>(n-1)/2){
res[i++]=nums[a--];
res[i++]=nums[b--];
}
if(a>=0) res[i++]=nums[a--];
if(b>n/2) res[i++]=nums[b--];
for(int j=0;j<n;j++) nums[j]=res[j];
}
};
注意点:
数组分两半为 0 ~(n-1)/2: (n-1)/2+1 ~ n-1;
正序穿插可能导致尾部重复,故需要倒序穿插;
快速选择代替sort排序(时间复杂度O(n))
快速选择——找到中位数;
并且将比中位数小的放在一边,比中位数大的放在一边;
与中位数相等的放在中间;
class Solution {
public:
void wiggleSort(vector<int>& nums) {
int mid=find_mid(nums,0,nums.size()-1); //找到中位数
//threepar(nums,mid);
insert_list(nums);
}
private:
void insert_list(vector<int>& nums){
int n=nums.size();
vector<int> res;
res.resize(n);
int a=(n-1)/2;
int b=n-1;
int i=0;
while(a>=0&&b>(n-1)/2){
res[i++]=nums[a--];
res[i++]=nums[b--];
}
if(a>=0) res[i++]=nums[a--];
if(b>n/2) res[i++]=nums[b--];
for(int j=0;j<n;j++) nums[j]=res[j];
}
int find_mid(vector<int>& nums,int left,int right){
if(right==left) return nums[left]; //放置下面除数为0
int pivot_index=left+rand()%(right-left); //left和right中间的随机下标
int p=partition(nums,pivot_index,left,right);
int tmp=(nums.size()+1)/2;
if(p==tmp) return nums[p];
else if(p>tmp) return find_mid(nums,left,p-1);
else return find_mid(nums,p+1,right);
}
int partition(vector<int>& nums,int pivot_index,int left,int right){
int pivot=nums[pivot_index];
int start_index=left;
swap(nums[pivot_index],nums[right]);
for(int i=left;i<right;i++)
if(nums[i]<pivot){
swap(nums[start_index],nums[i]); //小的换到前面
start_index++;
}
swap(nums[start_index],nums[right]);//换回来
return start_index;
}
};
快速选择+3way partition
C++提供的STL接口:nth_element(nums.begin(), midptr, nums.end());
可以把合适的数放置midptr位置上;
nth_element()函数进行快速选择,这一函数的效果是将数组中第n小的元素放在数组的第n个位置,同时保证其左侧元素不大于自身,右侧元素不小于自身。
threepar(nums,*midptr);
即可将nums进行3 way partition,将比*midptr小的数放在左边,大的放在右边;
class Solution {
public:
void wiggleSort(vector<int>& nums) {
auto midptr = nums.begin() + nums.size() / 2;
nth_element(nums.begin(), midptr, nums.end());
threepar(nums,*midptr);
insert_list(nums);
}
private:
void insert_list(vector<int>& nums){
int n=nums.size();
vector<int> res;
res.resize(n);
int a=(n-1)/2;
int b=n-1;
int i=0;
while(a>=0&&b>(n-1)/2){
res[i++]=nums[a--];
res[i++]=nums[b--];
}
if(a>=0) res[i++]=nums[a--];
if(b>n/2) res[i++]=nums[b--];
for(int j=0;j<n;j++) nums[j]=res[j];
}
void threepar(vector<int>& nums, int mid){
int left=0;
int right=nums.size()-1;
int cur=left;
while(cur<=right){
if(nums[cur]<mid)
swap(nums[cur++],nums[left++]);
else if(nums[cur]>mid)
swap(nums[cur],nums[right--]);
else cur++;
}
}
};
上述基础上加入虚地址
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++;
}
}
};
作者:hexcat
链接:https://leetcode-cn.com/problems/wiggle-sort-ii/solution/yi-bu-yi-bu-jiang-shi-jian-fu-za-du-cong-onlognjia/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。