324. 摆动排序 II
难度中等512
给你一个整数数组 nums
,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]...
的顺序。
你可以假设所有输入数组都可以得到满足题目要求的结果。
示例 1:
输入:nums = [1,5,1,1,6,4]
输出:[1,6,1,5,1,4]
解释:[1,4,1,5,1,6] 同样是符合题目要求的结果,可以被判题程序接受。
示例 2:
输入:nums = [1,3,2,2,3,1]
输出:[2,3,1,3,1,2]
提示:
1 <= nums.length <= 5 * 104
0 <= nums[i] <= 5000
- 题目数据保证,对于给定的输入
nums
,总能产生满足题目要求的结果
**进阶:**你能用 O(n) 时间复杂度和 / 或原地 O(1) 额外空间来实现吗?
排序+贪心【O(NlogN) + O(n)】
class Solution {
public void wiggleSort(int[] nums) {
int[] sortnums = nums.clone();
Arrays.sort(sortnums);
int n = nums.length;
int k = 0;
for(int i = n%2==0 ? n-2 : n-1; i >= 0; i -= 2){
nums[i] = sortnums[k++];
}
for(int i = n%2==0 ? n-1 : n-2; i >= 0; i -= 2){
nums[i] = sortnums[k++];
}
}
}
快速选择 + 三数排序
题解:https://leetcode.cn/problems/wiggle-sort-ii/solution/yi-bu-yi-bu-jiang-shi-jian-fu-za-du-cong-onlognjia/
我们发现,我们实际上并不关心A和B内部的元素顺序,只需要满足A和B长度相同(或相差1),且A中的元素小于等于B中的元素,且r出现在A的头部和B的尾部即可。
因此,我们第一步其实不需要进行排序,而只需要找到中位数即可。而寻找中位数可以用快速选择算法实现,时间复杂度为O(n)。
该算法与快速排序算法类似,在一次递归调用中,首先进行partition过程,即利用一个元素将原数组划分为两个子数组,然后将这一元素放在两个数组之间。两者区别在于快速排序接下来需要对左右两个子数组进行递归,而快速选择只需要对一侧子数组进行递归,所以快速选择的时间复杂度为O(n)。
找到中位数后,我们需要利用3-way-partition算法将中位数放在数组中部,同时将小于中位数的数放在左侧,大于中位数的数放在右侧。该算法与快速排序的partition过程也很类似,只需要在快速排序的partition过程的基础上,添加一个指针k用于定位大数
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;
}
}
在这一过程中,指针j和k从左右两侧同时出发相向而行,每次要么j移动一步,要么k移动一步,直到相遇为止。这一过程的时间复杂度显然为O(N)。
至此,原数组被分为3个部分,左侧为小于中位数的数,中间为中位数,右侧为大于中位数的数。之后的做法就与解法1相同了:我们只需要将数组从中间等分为2个部分,然后反序,穿插,即可得到最终结果。
java实现
class Solution {
int[] nums;
int n;
int qselect(int l, int r, int k){
if(l == r) return nums[l];
int pivot = nums[l + r >> 1];
int p1 = l - 1, p2 = r + 1;
while(p1 < p2){
do p1 ++; while(nums[p1] < pivot);
do p2 --; while(nums[p2] > pivot);
if(p1 < p2) swap(p1, p2);
}
int cnt = p2 - l + 1;
if(cnt < k) return qselect(p2 + 1, r, k - cnt);
else return qselect(l, p2, k);
}
void swap(int a, int b){
int c = nums[a];
nums[a] = nums[b];
nums[b] = c;
}
public void wiggleSort(int[] _nums) {
nums = _nums;
n = nums.length;
int p = qselect(0, n - 1, n + 1 >> 1);
int p1 = 0, p2 = n - 1, loc = 0;
while(loc <= p2){
if(nums[loc] < p){
swap(loc, p1);
loc ++; p1 ++;
} else if(nums[loc] > p){
swap(loc, p2);
p2 --;
} else loc ++;
}
int[] clone = nums.clone();
int idx = n - 1;
for(int i = 1; i < n; i = i + 2){
nums[i] = clone[idx];
idx --;
}
for(int i = 0; i < n; i = i + 2){
nums[i] = clone[idx];
idx --;
}
}
}