LeetCode 1338 - 数组大小减半 [贪心 + 排序](Python3|Go)

LeetCode 1338 - 数组大小减半 [贪心 + 排序](Python3|Go)
最新回答
梦似曾见

2020-11-26 06:02:30

答案:要解决这个问题,我们需要找到一个最小的数字集合,使得移除这些数字后,原数组中至少一半的数字被移除。可以通过贪心算法结合排序来实现,即优先移除出现次数最多的数字,直到移除的数字数量达到或超过数组的一半。

算法思路
  1. 统计频率:首先统计每个数字在数组中出现的次数。
  2. 排序频率:将数字按照出现次数从高到低排序。
  3. 贪心选择:从出现次数最多的数字开始,依次将数字加入集合,并累加移除的数字数量,直到移除的数字数量达到或超过数组的一半。
  4. 返回结果:集合的大小即为所求的最小大小。
解决代码Python3 实现from collections import Counterfrom typing import Listclass Solution: def minSetSize(self, arr: List[int]) -> int: n = len(arr) # 统计每个数字的出现次数 num_to_cnt = Counter(arr) # 将数字按出现次数降序排序 cnt_with_nums = sorted((-cnt, num) for num, cnt in num_to_cnt.items()) ans = 0 total = 0 for cnt, num in cnt_with_nums: ans += 1 total += -cnt if (total << 1) >= n: return ans return ansGo 实现import "sort"func minSetSize(arr []int) int { n := len(arr) // 统计每个数字的出现次数 numToCnt := make(map[int]int) for _, num := range arr { numToCnt[num]++ } // 将数字存入切片以便排序 nums := make([]int, 0, len(numToCnt)) for num := range numToCnt { nums = append(nums, num) } // 按出现次数降序排序 sort.Slice(nums, func(i, j int) bool { if numToCnt[nums[i]] != numToCnt[nums[j]] { return numToCnt[nums[i]] > numToCnt[nums[j]] } return nums[i] < nums[j] }) ans := 0 total := 0 for _, num := range nums { ans++ total += numToCnt[num] if (total << 1) >= n { return ans } } return -1}代码解释
  1. 统计频率

    Python 使用 Counter 类统计每个数字的出现次数。

    Go 使用 map[int]int 来统计每个数字的出现次数。

  2. 排序频率

    Python 将数字和其出现次数的负数组成元组,利用 sorted 函数按出现次数降序排序。

    Go 将数字存入切片,然后使用 sort.Slice 自定义排序规则,按出现次数降序排序。

  3. 贪心选择

    遍历排序后的数字,依次将数字加入集合,并累加移除的数字数量。

    每次累加后检查是否达到或超过数组的一半,如果是则返回当前集合的大小。

  4. 返回结果

    由于题目保证有解,循环中一定会返回结果,最后的 return -1 仅为语法需要。

这种方法确保了在最优情况下以最小的集合移除至少一半的数字,时间复杂度主要由排序决定,为 O(n log n),空间复杂度为 O(n)。