914. 卡牌分组
题目:
给定一副牌,每张牌上都写着一个整数。
此时,你需要选定一个数字 X,使我们可以将整副牌按下述规则分成 1 组或更多组:
每组都有 X 张牌。
组内所有的牌上都写着相同的整数。
仅当你可选的 X >= 2 时返回 true。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/x-of-a-kind-in-a-deck-of-cards
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
1.分析
读题知道「相同的数被分在一组」,每一组除了数值相同以外,我们还关心一个属性:这个组里元素的个数,很容易想到应该统计元素个数;
题目文字和示例 3 和告诉我们:如果检测到某个组里元素只有 1 个,可以直接返回 false。
示例 5 非常特殊,是一个很重要的示例,[2, 2, 2, 2] 硬是被拆成了 2 组,为的是与组 [1, 1] 的元素个数相等。这对应了题目「每组都有 X 张牌」 的要求。
2.设计
1、遍历一次,统计每个数值的个数,如果某个数值只有 1 个,直接返回 false;
2、再看一下示例 5,想更一般化的情况,输入 [2, 2, 2, 2, 3, 3, 3, 3, 3, 3],其实也是符合题意的分组,2 有 4 个,3 有 6 个,相同的 2 和 3 都需要拆成 2 个一组,因此这里的 X = 2,很显然 2 是这两个组的元素个数的公约数。
3、那么整体的思路就是求取每个数的count,求取这些count以及牌总数的最大公约数
4、求取最大公约数,必然要用到辗转相除法
private int gcd (int a, int b) {
return b == 0? a: gcd(b, a % b);
}
3.实现
package com.sq;
/**
* @Author: iamasd
* @Description:
* @Date: Created in 13:262020/3/27
*/
public class Test11 {
public static void main(String[] args) {
int[] deck = {1,2,3,4,4,3,2,1};
boolean b = hasGroupsSizeX(deck);
System.out.println(b);
}
public static boolean hasGroupsSizeX(int[] deck) {
// 计数
int[] counter = new int[10000];
for (int num: deck) {
counter[num]++;
}
// 求gcd
int x = 0;
for(int cnt: counter) {
if (cnt > 0) {
x = gcd(x, cnt);
if (x == 1) {
return false;
}
}
}
return x >= 2;
}
/*public boolean hasGroupsSizeX(int[] deck) {
// 计数
int[] counter = new int[10000];
for (int num: deck) {
counter[num]++;
}
// 求最大公约数
return Arrays.stream(counter).reduce(this::gcd).getAsInt() > 1;
}*/
// 辗转相除法
private static int gcd (int a, int b) {
return b == 0? a: gcd(b, a % b);
}
}
声明:java8 Stream写法采用了 Sweetiee 的代码题解
4.总结
1.这次的题个人感觉自己刚开始理解错误题目了,始终做不出来,最后借鉴了官方的题解,才明白题意。
2.虽说大部分crtl v c 了,但是,自己debug过代码,也理解了。
3.知行合一