解法
基本框架是要形成一个模式:
偶数位放较小数(前半),奇数位放较大数(后半),由于前半的最大和后半的最小有可能相等【它们一定都是中位数】,那么要把前半的最大和后半的最小分开,最好的方法就是分别放在两头
解法一:基于排序
首先得到排好序的数组,然后:
- 偶数位递减放置前半,这样前半的最大在左边
- 奇数位递减放置后半,这样后半的最小一定在右边
为什么不同时递增呢?这样不是就形成【前半的最大在左边,后半的最小在右边】的局面了吗?
- 假如
n=2k
,偶数编号[0,2k-2]
,奇数编号[1,2k-1]
。
- 如果取偶左奇右
[0,2k-1]
,即前半倒过来放,前半的最大就在0
,后半也倒过来放,后半的最小就在2k-1
,它们只有在k=1
时相邻,那么在保证能得到合法解的时候,如果k==1
,那么相邻的这两个数一定不相等。- 相反,如果取偶右奇左
[1,2k-2]
,即前半顺序放置,前半最大在2k-2
,而后半也顺序放置,后半的最小在1
,当k=1
的时候虽然相邻,但是由于保证解合法所以没问题,但是在k=2
的时候也相邻,这样就有可能出问题,比如在[4,5,5,6]
这样的情况下。- 假如
n=2k+1
,偶数编号[0,2k]
,奇数编号[1,2k-1]
。
- 如果取偶左奇右
[0,2k-1]
,它们只有在k=1
时相邻,那么在保证能得到合法解的时候,如果k==1
,那么相邻的这两个数就是三个数里最大的两个,要得到合法解,它们必然不相等。- 相反,如果取偶右奇左
[1,2k]
,同样当k=1
的时候会相邻,通过上述分析可知没问题
综上所述,就是数组长度为4是两边都顺序摆会有问题,所以理论上把数组排序后前半和后半都倒序然后分别摆在偶数和奇数位即可。
class Solution(object):
def wiggleSort(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
nums.sort()
c = (len(nums)-1)//2+1
nums[::2],nums[1::2] = nums[:c][::-1],nums[c:][::-1]
解法二:基于快排和三色排序
首先通过跟快排的partition相似的操作用
O
(
n
)
O(n)
O(n)的方法找到中位数
然后,数组就分成了三部分:【大于中位数、小于中位数、等于中位数】。
跟上个解法里的分析相似,小于中位数的部分要放到偶数位索引较大的位置上,大于中位数的部分要放到奇数位的较小索引上,剩下的位置就放等于中位数的部分。
也就是说,假如我们按1,3,5,7,...,0,2,4,6,8....
来遍历的话,整个数组会按【大于中位数、等于中位数、小于中位数】的顺序排列。
如何按照这个顺序遍历呢?
我们从0~n
遍历,对应的(2*i+1)%nn
就是这个顺序了,其中nn
为大于等于n
的最小奇数,当2*i+1
不超过nn
的时候遍历的是奇数,超过之后,由于除数也是奇数,取余之后作减法就是偶数了,所以达到按照上述顺序遍历的目的了。
然后回想一下三色排序的算法
维护0区域的下一个位置l
,2区域的下一个位置r
,以及当前遍历指针i
- 如果
a[i]==1
,前进一位 - 如果
a[i]==0
,将a[i]
和a[l]
交换,由于0的个数增加了,所以l
要增加;另外由于肯定有l<=i
,那么换到a[i]
位置上的a[l]
的值肯定是1,所以i
的值就直接增加就好 - 如果
a[i]==2
,那么将a[i]
和a[r]
交换,由于2的个数增加了,所以r
要减小,换到a[i]
上的数字有可能是0\1\2
所以i
就不增加了。
class Solution {
public:
int n;
void wiggleSort(vector<int>& a) {
n = a.size();
int tar = findkthnum(a,0,n-1,(n+1)>>1);
// printf("%d\n",tar);
int l=0,i =0,r = n-1;
n |= 1;
while (i<=r){
int ii=idx(i), ll=idx(l), rr=idx(r);
// printf("%d %d %d\n",ii,ll,rr);
if (a[ii]<tar) {
swap(a,ii,rr);
// printf("%d %d %d %d\n",a[0],a[1],a[2],a[3]);
r--;
}
else if (a[ii]>tar) {
swap(a,ii,ll);
// printf("%d %d %d %d\n",a[0],a[1],a[2],a[3]);
i++,l++;
}
else i++;
}
}
int idx(int a){
return ((a<<1)+1)%n;
}
int findkthnum(vector<int>& a, int l, int r, int k){
if (l==r) return a[l];
int now=l;
for(int i=l;i<r;i++) {
if (a[i]<a[r]) swap(a,i,now++);
}
swap(a,r,now);
if (k==now)
return a[k];
else if (k<now)
return findkthnum(a,l,now-1,k);
else
return findkthnum(a,now+1,r,k);
}
void swap(vector<int>& a, int i,int j) {
if (i!=j) {
a[i]^=a[j];a[j]^=a[i];a[i]^=a[j];
}
}
};