zoukankan      html  css  js  c++  java
  • [LeetCode] Wiggle Sort II | 摆动排序

    https://leetcode.com/problems/wiggle-sort-ii/#/description

    一开始想的方法是把数组复制一份,然后按最小、最大、最小...依次放数字,结果WA,错误代码:

    class Solution {
    public:
        void wiggleSort(vector<int>& nums) {
            int n = nums.size();
            if (n == 0) return;
            
            vector<int> nums_copy(nums.begin(), nums.end());
            sort(nums_copy.begin(), nums_copy.end());
            int k = 0, p = 0, q = n - 1;
            bool sign = true;  // smallest of the remaining numbers
            while (k < n) {
                if (sign) nums[k++] = nums_copy[p++];
                else nums[k++] = nums_copy[q--];
                sign = !sign;
            }
        }
    };
    

    比如下面这个例子:[1,3,2,2,3,1]

    按这个方法得到的输出是[1,3,1,3,2,2],最后两个数不符合要求。在做的过程中,最小和最大数的差距逐渐缩小,导致最后无法达成严格的大小关系。可见这种方法最大的困难在于如何避免连续两个数相等(甚至可行解就很少)。

    如果取完最小的,不取当前最大,取一个比较大的数?比如,这个“比较大的数”取排序完靠中间的。上面的例子,排序后是1,1,2, | 2,3,3,取右半边第一个数。但是还是有问题,比如4,5,5,6,按这个方法得到的是4,5,5,6,中间两个又相等了,当排序后中位数不止一个时,就会有这个问题。有效的克服办法是,小的从中间开始递减着取,大的从最大开始递减着取

    class Solution {
    public:
        void wiggleSort(vector<int>& nums) {
            int n = nums.size();
            if (n == 0) return;
            
            vector<int> nums_copy(nums.begin(), nums.end());
            sort(nums_copy.begin(), nums_copy.end());
            int k = 0, p = (n-1)/2, q = n-1;
            bool sign = true;  // smallest of the remaining numbers
            while (k < n) {
                if (sign) nums[k++] = nums_copy[p--];
                else nums[k++] = nums_copy[q--];
                sign = !sign;
            }
        }
    };
    

    能不能优化?比如,完整的排序有没有必要?再看上面的操作,其实我们是想把整个数组划分成左右两部分,使得左边的比右边的小,然后左、右交替着取。这样就自然联想到了快速排序的partition操作,但是这里最好能够将划分做得均衡,因此可以求kth largest element in an array ( https://leetcode.com/problems/kth-largest-element-in-an-array/ )。另外,除了均衡,仍然要克服中位数不止一个的情形,比如类似4,5,5,5,5,6,6,6的case,因此在做完划分最好将中位数调整一下位置:一种可行方法是把中位数全部挤到中间。

    下面的参考代码,偷懒一下,划分的操作就用std::nth_element代替了。其他的部分都没变,只是把原来的排序换成了nth_element() + refine(),其中refine()就是调整中位数位置的操作。

    时间复杂度:平均意义上是O(n)

    空间复杂度:O(n),O(1)的做法是在做完划分后交替着用交换构造结果

    class Solution {
    public:
        void wiggleSort(vector<int>& nums) {
            int n = nums.size();
            if (n == 0) return;
            
            vector<int> nums_copy(nums.begin(), nums.end());
            int mid = (n - 1) / 2;
            std::nth_element(nums_copy.begin(), nums_copy.begin() + mid, nums_copy.end());
            refine(nums_copy, mid);
            
            int k = 0, p = mid, q = n-1;
            bool sign = true;  // need a smaller number
            while (k < n) {
                if (sign) nums[k++] = nums_copy[p--];
                else nums[k++] = nums_copy[q--];
                sign = !sign;
            }
        }
    private:
        void refine(vector<int>& nums, int mid) {
            int put_here = 0, mid_val = nums[mid];
            // left part
            for (int i = 0; i < mid; ++i) {
                if (nums[i] != mid_val) nums[put_here++] = nums[i];
            }
            while (put_here < mid) nums[put_here++] = mid_val;
            // right part
            put_here = nums.size() - 1;
            for (int i = nums.size()-1; i > mid; --i) {
                if (nums[i] != mid_val) nums[put_here--] = nums[i];
            }
            while (put_here > mid) nums[put_here--] = mid_val;
        }
    };
    
  • 相关阅读:
    BINARY and varBINARY
    DECIMAL Data Type
    timestamp ---自动更新修改时间 与 记录首次插入时间
    可视化之Berkeley Earth
    可视化之Earth NullSchool
    数据可视化之风向图
    “System.Runtime.InteropServices.COMException”类型的第一次机会异常在 ESRI.ArcGIS.Version.dll 中发生
    如何用arcgis进行WGS84的投影坐标变换
    break和continue的区别
    MySql查询系统时间,SQLServer查询系统时间,Oracle查询系统时间
  • 原文地址:https://www.cnblogs.com/ilovezyg/p/6901280.html
Copyright © 2011-2022 走看看