每日題。一開始想錯了,想成平均數,正確應該是中位數才對。

題目

輸入長度n的陣列nums,求需要幾次動作才能使陣列中所有元素相同。
每次動作,你可以任選一個元素將其+1或是-1。

解法

陣列本身是無序的,得先排序,方便找到最大和最小值。
假設nums=[1,2,3,4],我們必須在1和4之間找到一個目標數t,將所有元素修改成t。 對[1,2,3,4]來說,不管t為多少,動作次數都是3次,那麼可以t除掉1和4,將t的候選範圍縮減到[2,3],以此類推,正好就是中位數的定義。

找到中位數median後,將所有元素和median的差加總就是答案。

class Solution:
    def minMoves2(self, nums: List[int]) -> int:
        nums.sort()
        mid=len(nums)//2
        median=nums[mid]
        return sum(abs(x-median) for x in nums)

使用quick select找中位數,不需要排序,整體複雜度降低到O(N)。但還是比不過內建函數,執行起來反而更慢。

class Solution:
    def minMoves2(self, nums: List[int]) -> int:
        
        def k_smallest(a,k):
            ls,eq,gt=[],[],[]
            x=random.choice(a)
            for n in a:
                if n<x:
                    ls.append(n)
                elif n>x:
                    gt.append(n)
                else:
                    eq.append(n)
            if k<=len(ls):
                return k_smallest(ls,k)
            elif k>len(ls)+len(eq):
                return k_smallest(gt,k-len(ls)-len(eq))
            else:
                return x
                
        median=k_smallest(nums,len(nums)//2+1)
        return sum(abs(median-n) for n in nums)