Easy
题目描述
给定一个整数数组 deck,其中 deck[i] 表示第 i 张卡牌上写的数字。
将卡牌分成一个或多个组,使得:
- 每组恰好有
x张卡牌,其中x > 1 - 一组内的所有卡牌上都写着相同的整数
如果可能分组,返回 true;否则返回 false。
示例 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 <= 1040 <= deck[i] < 104
解题思路
解题思路
这道题的核心在于理解题目要求:每个数字的卡牌都要分成大小相同的组,且组的大小必须大于1。
关键观察:
- 如果存在满足条件的分组方案,那么每个数字出现的次数都必须能被同一个数
x(x > 1)整除 - 换句话说,所有数字出现次数的最大公约数(GCD)必须大于1
解法一:最大公约数法(推荐)
- 统计每个数字出现的次数
- 求出所有次数的最大公约数
- 判断最大公约数是否大于1
解法二:暴力枚举法 枚举可能的组大小 x(从2到最小出现次数),检查是否所有次数都能被 x 整除。
算法步骤:
- 使用哈希表统计每个数字的出现次数
- 计算所有出现次数的最大公约数
- 返回最大公约数是否大于1
时间复杂度主要取决于计算GCD的过程,空间复杂度为统计频次所需的空间。
代码实现
class Solution {
public:
bool hasGroupsSizeX(vector<int>& deck) {
unordered_map<int, int> count;
for (int card : deck) {
count[card]++;
}
int gcd_result = 0;
for (auto& p : count) {
gcd_result = __gcd(gcd_result, p.second);
}
return gcd_result >= 2;
}
};
class Solution:
def hasGroupsSizeX(self, deck: List[int]) -> bool:
from collections import Counter
import math
count = Counter(deck)
gcd_result = 0
for freq in count.values():
gcd_result = math.gcd(gcd_result, freq)
return gcd_result >= 2
public class Solution {
public bool HasGroupsSizeX(int[] deck) {
var count = new Dictionary<int, int>();
foreach (int card in deck) {
count[card] = count.GetValueOrDefault(card, 0) + 1;
}
int gcdResult = 0;
foreach (var freq in count.Values) {
gcdResult = GCD(gcdResult, freq);
}
return gcdResult >= 2;
}
private int GCD(int a, int b) {
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
}
var hasGroupsSizeX = function(deck) {
const count = {};
for (const card of deck) {
count[card] = (count[card] || 0) + 1;
}
const gcd = (a, b) => {
while (b !== 0) {
[a, b] = [b, a % b];
}
return a;
};
let gcdResult = 0;
for (const freq of Object.values(count)) {
gcdResult = gcd(gcdResult, freq);
}
return gcdResult >= 2;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n + k·log(min(freq))) | n为卡牌数量,k为不同数字种类,log项为计算GCD的复杂度 |
| 空间复杂度 | O(k) | k为不同数字的种类数,用于存储频次统计 |