欢迎使用CSDN-markdown编辑器

leetcode | 324. Wiggle Sort II

一、问题描述
链接:https://leetcode.com/problems/wiggle-sort-ii/#/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?

二、思路及代码
根据问题描述,可知结果序列要求奇数项大于两边的偶数项。所以在暂时忽略follow up要求的情况下,第一个思路是将原序列从小到大排序后分成两段,将较大的一半反向插入到较小的中间,则较大段一定大于两边的元素。即:If 0 < 1 < … < n, then 0 < n > 1 < n-1 > 2 … must be true. 另外,分段的时候需要考虑奇数和偶数的情况,关键在于奇数段的一半应该分在哪里:

    偶数:
        排序后:123456
        分段后:123 | 4,5,6
        重组后:162534

    奇数:
        排序后:1234567
        分段1后:1234 | 5,6,7
        重组后:1726354
        k个较大数插入k+1个较小数中间,肯定可以满足题目要求,且容易理解。

        还有第二种分段方法:123 | 4,5,6,7
        重组后:1726354
        假如保持首尾插入法不变,这种方法得到的最后答案是一样的,因为中间的那个数总是在最末尾。但使用其他插入方法时,这种分段法可能会失效。
    所以我采取了第一种分段法,即奇数长度的列表把中间的数算作较小数,在Leetcode运行了第一版代码如下:
class Solution(object):
    def wiggleSort(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        tmplist = []
        n = len(nums)
        nums.sort()
        for i in range(0, n/2 + 1):
            tmplist.append(nums[i])
            if n-i != i:
                tmplist.append(nums[n-i-1])
        nums = tmplist

上面这段代码是错误的。运行案例[1,5,1,1,6,4]后结果是[1,1,1,4,5,6]而非[1,6,1,5,1,4],看似没有经过for循环。在本地运行这段代码,结果是正确的。经过网友@寒哥指点,发现是python赋值原理的原因导致nums指针变化,而原来的指针指向的位置的数据是仅经过排序的(详情请见第三部分)。
后来把nums = tmplist这句话改成了:

for i in range(0, n):
     nums[i] = tmplist[i]

就可以通过上面这个案例了。

但是仍然有错误,在提交的时候,奇数案例[1,1,2,1,2,2,1]报错最后一行代码数组长度溢出。经过排查,发现第一个for循环中的range上界取7/2时运算结果是3(详情见三)。经过修改后得到如下代码:

import math

class Solution(object):
    def wiggleSort(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.

        :method: If 0 < 1 < ... < n, then 0 < n > 1 < n-1 > 2 ... must be true. So we can firstly sort the list into an ascending order. Then append the smallest element to the temp list, and then the largest one, and so on... 
            The time complexity mainly depends on that of the sort function, and its average complexity is O(nlogn) here.
        """
        tmplist = []
        n = len(nums)
        half = int(math.ceil(n/float(2)))
        nums.sort()
        for i in range(0, half):
            tmplist.append(nums[i])
            if n-i-1 != i:
                tmplist.append(nums[n-i-1])
        for i in range(0, n):
            nums[i] = tmplist[i]

提交上述代码,在输入案例[1,3,2,2,3,1]时报错,输出应为[2,3,1,3,1,2],而上述代码输出为[1,3,1,3,2,2]。
这时意识到我的算法的假设是错误的。即If 0 < 1 < … < n不一定成立,因为还有其中部分元素相等且分别处于较大数和较小数的情况会出现。在这个案例中,2就是这样的尴尬数:

排序后:112233
分段后:112 | 2,3,3
重组后:131322

可见之前我的处理方法是将排序后的列表分为两段,然后一个从首一个从尾部插入。这样的方法虽然最大程度保证了重组后的列表前面的数据之间的差足够大,但是没考虑到尾部之间的差值。所以我应该保证尾部的插值也足够大,所以为了解决从这个案例中发现的问题,又采取了分为两段后,为了让近似大小的元素尽可能分开,分别从两段头部取元素依次插入新列表的方法,即:

排序后:112233
分段后:112 | 2,3,3
重组后:121323

再次提交后,在案例[4,5,5,6]中报错,输出应为[5,6,4,5],而上述算法输出为[4,5,5,6]。所以交替插入应以降序进行,否则较小数段末尾和较大数段开头还会挨在一起。于是得到以下AC代码,复杂度主要取决于排序算法,为O(nlogn)。

import math

class Solution(object):
    def wiggleSort(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.
        :method: If 0 < 1 < ... < n, then 0 < n > 1 < n-1 > 2 ... must be true. So we can firstly sort the list into an ascending order. Then append the smallest element to the temp list, and then the largest one, and so on... 
            The time complexity mainly depends on that of the sort function, and its average complexity is O(nlogn) here.
        """
        tmplist = []
        n = len(nums)
        half = int(math.ceil(n/float(2)))
        nums.sort()
        for i in range(0, half):
            tmplist.append(nums[half-i-1])
            if n-i-1 > half - 1:
                tmplist.append(nums[n-i-1])
        for i in range(0, n):
            nums[i] = tmplist[i]

本以为O(n)的算法是只要把数据分为两段即可,不必排序,但现在经过横跨两段的相等的数的洗礼,觉得这种方法不太靠谱。O(n)的算法暂时没想出来,日后再更~

三、注意事项
1. python的赋值原理
在高级语言中,变量是对内存及其地址的抽象。对于python而言,python的一切变量都是对象,变量的存储,采用了引用语义的方式,存储的只是一个变量的值所在的内存地址,而不是这个变量的只本身。
这里写图片描述
参考链接:
http://www.cnblogs.com/work115/p/5619541.html
https://my.oschina.net/leejun2005/blog/145911

2.python的除法,range和ceil
python的除法(/),如整数除法7/2=3,浮点数除法7/float(2)=3.5。
range(a, b, step)均需要为int型。
math.ceil()为向上取整函数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值