Leetcode 1338:数组大小减半(超详细的解法!!!)

给你一个整数数组 arr。你可以从中选出一个整数集合,并删除这些整数在数组中的每次出现。

返回 至少 能删除数组中的一半整数的整数集合的最小大小。

示例 1:

输入:arr = [3,3,3,3,5,5,5,2,2,7]
输出:2
解释:选择 {3,7} 使得结果数组为 [5,5,5,2,2]、长度为 5(原数组长度的一半)。
大小为 2 的可行集合有 {3,5},{3,2},{5,2}。
选择 {2,7} 是不可行的,它的结果数组为 [3,3,3,3,5,5,5],新数组长度大于原数组的二分之一。

示例 2:

输入:arr = [7,7,7,7,7,7]
输出:1
解释:我们只能选择集合 {7},结果数组为空。

示例 3:

输入:arr = [1,9]
输出:1

示例 4:

输入:arr = [1000,1000,3,7]
输出:1

示例 5:

输入:arr = [1,2,3,4,5,6,7,8,9,10]
输出:5

提示:

  • 1 <= arr.length <= 10^5
  • arr.length 为偶数
  • 1 <= arr[i] <= 10^5

解题思路

一个比较容易想到的思路就是采用贪心策略,分别统计所有元素出现的次数,然后从出现次数最多的元素开始取,直到取得元素个数超过len(arr)//2

class Solution:
    def minSetSize(self, arr: List[int]) -> int:
        cnt = sorted(collections.Counter(arr).values(), reverse=True)
        t, n = 0, len(arr)
        
        for i, v in enumerate(cnt):
            t += v
            if t >= n//2:
                return i + 1
        return len(cnt)

实际上还可以处理的更快。我们可以统计元素出现的次数的频率cnt,然后将此频率按照元素出现的次数从大到小的顺序排序。这样我们就不用通过一步一步加来确定什么时候到达n//2,而是可以通过元素出现的次数乘以频率来确定。例如:

3,3,3,3,5,5,5,2,2,7
freq:	3:4 5:3 2:2 7:1
cnt:	4:1 3:1 2:1 1:1

代码如下:

class Solution:
    def minSetSize(self, arr: List[int]) -> int:
        n, total, res = len(arr) // 2, 0, 0
        freq = collections.Counter(arr)
        cnt = collections.Counter(freq.values())
        
        for i in sorted(cnt.keys(), reverse=True):
            dif = n - total
            x = min(cnt[i], (dif + i - 1)//i)
            res += x
            total += x * i
            if total >= n:
                return res
        return res

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

发布了731 篇原创文章 · 获赞 457 · 访问量 83万+

猜你喜欢

转载自blog.csdn.net/qq_17550379/article/details/104165693
今日推荐