【Leetcode】462. Minimum Moves to Equal Array Elements II 462. 最少移动次数使数组元素相等 II

1

解法

记录一下,是个好题,解法超多
首先需要明确的是,最后相同的那个数值一定是数组中的元素,因为这样能省下一些跳数

解法一:暴力

解法二:排序+求和

当我们以nums[i]作为最后的结果的时候,那么前面的数都不超过nums[i],所以前面的数的总步骤为nums[i]*i-sum(nums[:i])
后面的数都不小于nums[i],后面的数的总步骤为sum(nums[i:])-nums[i]*(n-i)
和可以事先求好,最后选出最小的就行

class Solution(object):
    def minMoves2(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        nums.sort()
        n = len(nums)
        p = [0]
        for a in nums:
            p.append(p[-1]+a)
        ans = 0x7fffffff
        for i,a in enumerate(nums):
            ans = min(ans, a*i-p[i]+p[-1]-p[i+1]-a*(n-i-1))
        return ans

解法三:排序+中位数

接下来要到这个题的一个重要性质了,最后那个相同数值一定就是数组的中位数
为什么呢?
假设我们在中位数为m的条件下算出一个解moves
不防假设小于目标值的数字个数为k1,大于目标值的数字个数为k2

  • 如果我们要把目标值变小h,即最后数组里的数都变成m-h,那么会有k1个数的步数减少h,但是会有k2个数的步数增大h,由于m-h在中位数的左边,所以肯定有k1<k2,所以最后相比于目标值为中位数得到的moves会增加
  • 同理,如果我们把目标值增大h,那么会有k1个数增加h,有k2个数减少h,而由于又有k1>k2,所有最后表现还是增加

综上,中位数时候的值是最小的

可以直接排序找中位数

class Solution(object):
    def minMoves2(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        nums.sort()
        n = len(nums)
        j = n//2
        mid = nums[j]
        return mid*j-sum(nums[:j])+sum(nums[j:])-mid*(n-j)

解法四:基于快排找中位数

套路算法辽……,由于用来partition的值如果经常不幸选中成了边上的元素,这样算法会退化成 O ( n 2 ) O(n^2) O(n2),有可能会超时
直接加了个简单的随机……

class Solution(object):
    def minMoves2(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        import random
        n = len(nums)
        def partition(l,r,k):
            rr = random.randint(l,r)
            nums[rr],nums[r] = nums[r],nums[rr]
            ll = l
            for i in xrange(l,r):
                if nums[i]<nums[r]:
                    nums[ll],nums[i] = nums[i],nums[ll]
                    ll += 1
            nums[ll],nums[r] = nums[r],nums[ll]
            if ll==k:
                return nums[ll]
            if ll<k:
                return partition(ll+1,r,k)
            return partition(l,ll-1,k)
        j = n//2
        tar = partition(0,n-1,j)
        return tar*j-sum(nums[:j])+sum(nums[j:])-tar*(n-j)

解法五:排序但不找中位数

不管最后统一成什么数,我们假设为k,那么数组的最大值max和最小值min,一定一个在k上一个在k下,那么它俩的步数加起来就是:
max-k+k-min=max-min
跟具体是目标值是啥没关系了
所以我们可以一对一对地算步数,这样就不需要找目标值了

class Solution(object):
    def minMoves2(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        nums.sort()
        ans = 0
        l,r = 0,len(nums)-1
        while l<r:
            ans += nums[r]-nums[l]
            l += 1
            r -= 1
        return ans
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值