题目:

给定一个无序的数组 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) 额外空间来实现吗?

O(nlogn)的解法:

想到了先排序然后取前半部后半部交叉赋值,但第41个用例过不去,是一个[4,5,5,6],看了大佬的解答,将两部分倒置再穿插赋值,这样就可以避免中间相邻的两个元素(前半部最大的数a和后半部最小的数b)相等的情况,因为两边分别倒置后,a在最前,b在最后,不会再组成一对插入新数组,很巧妙,另外还要注意c+的reverse是[begin,end)半闭半开区间,

 1 void wiggleSort(vector<int>& nums)
2 {
3 int siz = nums.size();
4 sort(nums.begin(), nums.end());
5 vector<int>temp(nums.size(),0);
6 reverse(nums.begin(), nums.begin() + (siz - 1) / 2+1);
7 reverse(nums.begin() + (siz - 1) / 2+1, nums.end());
8 int i=0,m = 0, n = (siz - 1) / 2 + 1;
9 for (;n<siz;++m,++n)
10 {
11 temp[i++] = nums[m];
12 temp[i++] = nums[n];
13 }
14 if (m == (siz - 1) / 2)
15 {
16 temp[i] = nums[m];
17 }
18 nums = temp;
19 }

O(n)的解法:

前面排序后交叉赋值的方法做完后,应该可以想到前半段和后半段中并不要求严格有序,只要前面比后面的数都小就行,内部顺序其实无所谓。

那么就考虑用划分的方法,只要前面比后面小即可。

但普通快排用的划分是不行的,我们需要把数组分为恰好两半以交叉赋值回原数组。所以我们先要找中位数,再把这个中位数作为划分的基准数进行划分,这样划分出来的数组左右长度就应该是一样的。

中位数可以利用STL的nth_element()函数,时间O(N)。

然后简单的一次划分就行了吗?答案是某些情况可以(即中位数只出现一次的情况),但多数情况不可以。比如这个数组:[1,3,2,2,3,1]

其长度为6,中位数是2,对其做一次正常划分,下面这段代码是算法导论上的partition函数:

//之前先swap(nums[n/2],nums[ri]),以将基准数换为求好的中位数(n为数组长度)
1 int partition_2(vector<int>& nums,int le,int ri){ //算法导论的方法
2 int stable=nums[ri];
3 int i=le-1,j=le;
4 while(j<ri){
5 if(nums[j]<stable){
6 swap(nums[j],nums[++i]);
7 }
8 ++j;
9 }
10 swap(nums[i+1],nums[ri]);
11 return i+1;
12 }

对其划分之后,数组为:1 1 2 3 3 2

可以看到,如果取该结果数组的前后各一半,进行交叉赋值是得不到摆动排序的数组的。原因是中位数2存在多个,我们的划分算法中对于nums[j]<=stable的处理(第6行)是简单的把小于基准数的数字都放到数组前面。最终的效果就是中位数和小于中位数的数字可能会乱序交叉在一起(例子中2,2,1,1就乱序了)。(当然如果我们划分算法里把小于等于基准数的数字都放到数组前面的话,那最终中位数同样的会可能和大于中位数的数字乱序交叉在一起。)我们希望的是中位数全部好好待在数组中部,左侧的数字都小于中位数,右侧的数字都大于中位数。那么就引出了一个新概念:三路划分,即将小于某个数的数字都排在左边,大于的都排在右边,中间的是该数字,不论它有几个。这个问题也就是https://leetcode-cn.com/problems/sort-colors/

下面是本题正确划分的代码:

其中i是最后一个小于中位数的数字的右侧,j是第一个小于中位数的数字的左侧,换句话:[0,i)是小于中位数的,[i,j]是等于中位数的,(j,n-1]是大于中位数的

经过如下划分算法,我们就可以避免上述的问题,还是上面的数据,经过一次划分后,数组为:1 1 2 2 3 3

int i=0,j=n-1,cur=0;
while(cur<j){
if(nums[cur]<mid){
swap(nums[i++],nums[cur]);
}
else if(nums[cur]>mid){
swap(nums[j--],nums[cur]);
}
++cur;
}

完整代码为:

 1 class Solution {
2 public:
3 void wiggleSort(vector<int>& nums) {
4 int n=nums.size();
5 nth_element(nums.begin(),nums.begin()+n/2,nums.end());
6 int mid=nums[n/2];
7 //划分
8 int i=0,j=n-1,cur=0;
9 while(cur<j){
10 if(nums[cur]<mid){
11 swap(nums[i++],nums[cur]);
12 }
13 else if(nums[cur]>mid){
14 swap(nums[j--],nums[cur]);
15 }
16 ++cur;
17 }
18 int l=n%2?n/2+1:n/2;
19 vector<int> p1(nums.begin(),nums.begin()+l);
20 vector<int> p2(nums.begin()+l,nums.end());
21 for(int i=0;i<p1.size();++i){
22 nums[2*i]=p1[p1.size()-i-1];
23 }
24 for(int i=0;i<p2.size();++i){
25 nums[2*i+1]=p2[p2.size()-i-1];
26 }
27 }
28 };

可以看到,划分算法更快:

完结撒花

324. 摆动排序 II(三路划分算法)的更多相关文章

  1. Java实现 LeetCode 324 摆动排序 II

    324. 摆动排序 II 给定一个无序的数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]- 的顺序. 示例 1: 输入: n ...

  2. Leetcode 324.摆动排序II

    摆动排序II 给定一个无序的数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序. 示例 1: 输入: nums ...

  3. LeetCode——324. 摆动排序 II

    给定一个无序的数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序. 示例 1: 输入: nums = [1, 5 ...

  4. [LeetCode] 324. Wiggle Sort II 摆动排序 II

    Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3]... ...

  5. [Leetcode] 第324题 摆动排序II

    一.题目描述 给定一个无序的数组 nums,将它重新排列成 nums[0] < nums[1] > nums[2] < nums[3]... 的顺序. 示例 1: 输入: nums ...

  6. 324 Wiggle Sort II 摆动排序 II

    给定一个无序的数组nums,将它重新排列成nums[0] < nums[1] > nums[2] < nums[3]...的顺序.例子:(1) 给定nums = [1, 5, 1, ...

  7. leetcode324 摆动排序II

      1. 首先考虑排序后交替插入 首尾交替插入,这种方法对于有重复数字的数组不可行: class Solution { public: void wiggleSort(vector<int> ...

  8. [Swift]LeetCode324. 摆动排序 II | Wiggle Sort II

    Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3]... ...

  9. [LeetCode] 280. Wiggle Sort 摆动排序

    Given an unsorted array nums, reorder it in-place such that nums[0] <= nums[1] >= nums[2] < ...

随机推荐

  1. docker配置仓库源

    1 修改docker配置文件 下面的内网ip改成公司的私有仓库地址 后面两个建议保留(一个是国内加速源,一个是国外仓库.这两个删了也是可以的) 2 重启docker服务 # vim /etc/dock ...

  2. 15.python文件(file)方法详解

    文件的基本操作 文件读写: 文件的读写满足以下3个步骤: 1).打开文件 2).操作数据(读.写) 3).关闭文件 --> 不要忘记 1).打开文件: python的open() 方法用于打开一 ...

  3. vue中报错Do not use built-in or reserved HTML elements as component id details

    原因是定义了一个叫做details的comonent 跟现有的html网页中的标签重合 export default { name: 'details', data () { return { equ ...

  4. Linux学习记录(一):常用命令

    此篇文章为博主在Linux学习过程中的一些记录,记录在此方便查阅. 常用命令总结 ls 显示目录内容 (list directory contents) ls 不显示隐藏的文件和子目录 ls -a 显 ...

  5. 吴裕雄--天生自然 R数据分析:2014年美国人时间使用调查(ATUS)饮食与健康模块文件分析

    # libraries we'll need library(car) # for avplots library(tidyverse) # for general utility functions ...

  6. 【新人赛】阿里云恶意程序检测 -- 实践记录11.3 - n-gram模型调参

    主要工作 本周主要是跑了下n-gram模型,并调了下参数.大概看了几篇论文,有几个处理方法不错,准备下周代码实现一下. xgboost参数设置为: param = {'max_depth': 6, ' ...

  7. 本地连接mysql的url写法

    一.jdbc:mysql:///中三条斜杠(///) 第三个/代表什么? jdbc:mysql:///testdatabase等同于 jdbc:mysql://localhost:3306/testd ...

  8. Spark性能优化指南——初级篇

    原文来我的公众号:Spark性能优化指南——初级篇 一. Spark作业原理 我们使用spark-submit提交一个Spark作业之后,这个作业就会启动一个对应的Driver进程.该进程是向集群管理 ...

  9. urllib模块提供的urlretrieve()函数使用

    urllib模块提供的urlretrieve()函数,urlretrieve()方法直接将远程的数据下载到本地 注意:若是网站有反爬虫的话这个函数会返回 403 Forbidden 参数url:传入的 ...

  10. ubuntu set up 5 - VIM

    Edit ~/.vimrc source vimrc: :so ~/.vimrc 1. ctrl - left/right 切换tabs https://vim.fandom.com/wiki/Usi ...