LeetCode 324. Wiggle Sort II

蝸牛一步一步往上爬發表於2016-09-03

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]

[2,1,3,6,5,7,3]
,目標數為3,則劃分結果應該為[2,1,3,3,6,5,7]
[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

相關文章