leetcode
leetcode 801 ~ 850
卡牌分组

卡牌分组

难度:

标签:

题目描述

代码结果

运行时间: 28 ms, 内存: 16.9 MB


/*
 * 思路:
 * 1. 使用Java Stream来统计每个数字出现的频率。
 * 2. 通过reduce方法计算这些频率的最大公约数(GCD)。
 * 3. 如果GCD >= 2,则可以进行分组,返回true;否则返回false。
 */

import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;

public class Solution {
    public boolean hasGroupsSizeX(int[] deck) {
        // 统计每个数字的频率
        Map<Integer, Long> count = Arrays.stream(deck)
            .boxed()
            .collect(Collectors.groupingBy(e -> e, Collectors.counting()));
        
        // 计算所有频率的最大公约数
        int gcdValue = count.values().stream()
            .mapToInt(Long::intValue)
            .reduce(-1, (a, b) -> a == -1 ? b : gcd(a, b));
        
        // 如果最大公约数>=2,则返回true,否则返回false
        return gcdValue >= 2;
    }
    
    // 计算两个数的最大公约数
    private int gcd(int a, int b) {
        if (b == 0) return a;
        return gcd(b, a % b);
    }
}

解释

方法:

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

时间复杂度:

O(n + k)

空间复杂度:

O(k)

代码细节讲解

🦆
为什么在这个问题中选择使用Counter类来统计各个数字的出现次数,而不是其他数据结构?
Counter类是Python中collections模块提供的一个专门用于计数的数据结构,它能够自动为每个出现的元素创建键并跟踪其计数,非常适合用于统计频率或出现次数的场景。在这个问题中,需要统计每种牌出现的次数,Counter类可以直接返回一个包含所有牌及其出现次数的字典,简化代码并提高效率。使用数组或普通字典虽然也可以完成相同的任务,但会需要手动处理不存在的键的情况,且代码更加繁琐。
🦆
题解中提到使用reduce和gcd来计算最大公约数,能否解释gcd如何应用在不同数目的牌之间以确定是否可以分组?
在这个问题中,我们需要确定是否可以将所有的牌分成若干组,且每组中的牌的数量相同。利用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,表示无法将牌分成每组至少两张的组。
🦆
题解中使用reduce函数结合gcd方法,这种方法在实际应用中是否总是有效?例如,如果计数值很大,计算gcd会有性能问题吗?
gcd函数通常非常高效,因为它基于辗转相除法(欧几里得算法),该算法在时间复杂度上是对数级别的。即使面对非常大的数,欧几里得算法也能快速找到它们的最大公约数。然而,在极端情况下,如果计数值非常大且数量很多,reduce结合多次gcd运算可能会有性能瓶颈。尽管如此,在大多数实际应用场景中,这种方法是有效且效率足够高的。

相关问题