卡牌分组

标签: 数组 哈希表 数学 计数 数论

难度: Easy

给定一副牌,每张牌上都写着一个整数。

此时,你需要选定一个数字 X,使我们可以将整副牌按下述规则分成 1 组或更多组:

  • 每组都有 X 张牌。
  • 组内所有的牌上都写着相同的整数。

仅当你可选的 X >= 2 时返回 true

示例 1:

输入:deck = [1,2,3,4,4,3,2,1]
输出:true
解释:可行的分组是 [1,1],[2,2],[3,3],[4,4]

示例 2:

输入:deck = [1,1,1,2,2,2,3,3]
输出:false
解释:没有满足要求的分组。


提示:

  • 1 <= deck.length <= 104
  • 0 <= deck[i] < 104

Submission

运行时间: 28 ms

内存: 16.9 MB

class Solution:
    def hasGroupsSizeX(self, deck: List[int]) -> bool:
        cnt = Counter(deck)
        return reduce(gcd, cnt.values()) >= 2

Explain

该题解首先使用Counter类来统计每个数字在牌组中出现的次数。然后使用reduce函数结合gcd(最大公约数)方法来计算所有计数值的最大公约数。如果这个最大公约数大于或等于2,意味着所有的牌都可以按照这个数量分组,每组牌的数量都是相同的。

时间复杂度: O(n + k)

空间复杂度: O(k)

class Solution:
    def hasGroupsSizeX(self, deck: List[int]) -> bool:
        # 使用Counter统计每种牌的数量
        cnt = Counter(deck)
        # 使用reduce和gcd函数计算所有计数的最大公约数
        # 并检查这个最大公约数是否至少为2
        return reduce(gcd, cnt.values()) >= 2

Explore

Counter类是Python中collections模块提供的一个专门用于计数的数据结构,它能够自动为每个出现的元素创建键并跟踪其计数,非常适合用于统计频率或出现次数的场景。在这个问题中,需要统计每种牌出现的次数,Counter类可以直接返回一个包含所有牌及其出现次数的字典,简化代码并提高效率。使用数组或普通字典虽然也可以完成相同的任务,但会需要手动处理不存在的键的情况,且代码更加繁琐。

在这个问题中,我们需要确定是否可以将所有的牌分成若干组,且每组中的牌的数量相同。利用gcd(最大公约数)是基于这样的事实:如果每种牌的数量的最大公约数大于1,那么可以将这些牌分为多组,每组牌的数量等于这个最大公约数。通过reduce函数结合gcd,可以逐步计算多个数(这里是各种牌的数量)的最大公约数。例如,有三种牌,数量分别为6,8和10,首先计算6和8的gcd为2,然后再将此结果2与10的gcd计算,得到的结果仍为2,表明可以将牌分为每组2张的多个组。

如果牌堆中某种牌的数量为1,这将直接影响gcd的计算结果。由于任何数和1的gcd都是1,这意味着如果任何一种牌的数量为1,那么所有数量的gcd也将是1。因此,如果题解中计算出的最大公约数是1,根据题解的逻辑,返回值将是false,表示无法将牌分成每组至少两张的组。

gcd函数通常非常高效,因为它基于辗转相除法(欧几里得算法),该算法在时间复杂度上是对数级别的。即使面对非常大的数,欧几里得算法也能快速找到它们的最大公约数。然而,在极端情况下,如果计数值非常大且数量很多,reduce结合多次gcd运算可能会有性能瓶颈。尽管如此,在大多数实际应用场景中,这种方法是有效且效率足够高的。