LeetCode 324. Wiggle Sort II


Problem Description

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

Example:
(1) Given nums = [1, 5, 1, 1, 6, 4], one possible answer is [1, 4, 1, 5, 1, 6].
(2) Given nums = [1, 3, 2, 2, 3, 1], 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?


Solution

这个题目看似简单,实际上是比较难的。
一开始的想法是找到数组的中位数,然后根据中位数对数组划分为两部分。大的那一部分放在索引为奇数的位置(1,3,5,7,9…),小的那一部分放在索引为偶数的位置(0,2,4,6,8…)。(这里是可以随意放的)
这个想法是对的,但还不够。比如当数组为[4,5,5,6]时,得到的结果就是[4,5,5,6],这不符合题目要求,正确的结果应该是[5,6,4,5]。这里问题出在两部分数中相同大小的数放在一起了。
为了避免这个问题,直观上来说,需要把较小的那一部分中比较大的数和较大的那一部分中比较小的数放的越远越好。(要做到这样有点难,因为这样需要两部分的数都有序,实际上一步划分之后两部分的数是无序的)
当然还一个简单的方法,那就是确保和中位数相等的数间隔放置的。
这里用到了three-way partitioning算法。

three-way partitioning

给定一个数组,和一个划分数组用的目标数,将数组划分为小于,等于,大于目标数三部分。
比如对于数组 [2,1,3,6,5,7,3] ,目标数为3,则划分结果应该为 [2,1,3,3,6,5,7]


#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

int main()
{
   vector<int> nums{2,1,3,6,5,7,3};
   int beg=0, end=nums.size()-1, i=0;
   int target=3;
   while(i<=end){
       if(nums[i]<target) swap(nums[i++],nums[beg++]);
       else if(nums[i]>target) swap(nums[i], nums[end--]);
       else ++i;
   }

   for_each(nums.begin(), nums.end(), [](int i){cout<<i<<" ";});
   cout<<endl;
   return 0;
}


看懂上面的代码之后就可以比较容易的理解下面的代码了。

class Solution
{
public:
  void wiggleSort( vector<int>& nums )
  {
      if(nums.empty()) return;
      int n = static_cast<int>(nums.size());

      //find the median
      auto midPtr = nums.begin()+ n/2;
      nth_element(nums.begin(), midPtr, nums.end());
      int median = *midPtr;

      //lambda: remap the idnex
      auto m = [n](int idx){return (2*idx+1)%(n|1);};

      //3-way-partition
      int beg = 0, end = n - 1, cur = 0;
      while(cur <= end){
          if( nums[m(cur)] > median ){
              swap(nums[m(cur++)], nums[m(beg++)]);
          }
          else if(nums[m(cur)] < median){
              swap(nums[m(cur)], nums[m(end--)]);
          }
          else ++cur;
      }
  }
};

lambda表达式是用来重新映射数组索引的。
Accessing m(0) actually accesses nums[1].
Accessing m(1) actually accesses nums[3].
Accessing m(2) actually accesses nums[5].
Accessing m(3) actually accesses nums[7].
Accessing m(4) actually accesses nums[9].
Accessing m(5) actually accesses nums[0].
Accessing m(6) actually accesses nums[2].
Accessing m(7) actually accesses nums[4].
Accessing m(8) actually accesses nums[6].
Accessing m(9) actually accesses nums[8].
这样的话,就自然而言在划分数组的时候,把较小的一部分放在了奇数位置,较大的一部分放在了偶数位置,并且因为每次都是间隔了一个数,中间相同的数就会被隔开。

参考

https://discuss.leetcode.com/topic/32929/o-n-o-1-after-median-virtual-indexing/2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值