卡牌分组
- https://leetcode.cn/problems/x-of-a-kind-in-a-deck-of-cards/
描述
-
给定一副牌,每张牌上都写着一个整数。
-
此时,你需要选定一个数字 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 <= 10410^4104
- 0 <= deck[i] < 10410^4104
Typescript 版算法实现
1 )方案1: 暴力解法
function hasGroupsSizeX(deck: number[]): boolean {
// 创建一个Map,用于存储每种牌的数量
const count: Map<number, number> = new Map();
// 统计每种牌的数量
for (const card of deck) {
count.set(card, (count.get(card) || 0) + 1);
}
// 牌的总数
const N: number = deck.length;
// 从2到N遍历,尝试不同的分组大小
for (let X = 2; X <= N; X++) {
// 如果N可以被X整除,则有可能将牌分成X张一组
if (N % X === 0) {
// 检查每种牌的数量是否都能被X整除
let allDivisible = true;
for (const val of count.values()) {
if (val % X !== 0) {
// 如果有任何一种牌的数量不能被X整除,则无法分组
allDivisible = false;
break;
}
}
// 如果所有牌的数量都能被X整除,则可以分组
if (allDivisible) {
return true;
}
}
}
// 所有可能的分组大小都无法分组,返回false
return false;
}
- 这个暴力解法的思路是通过尝试所有可能的分组大小来判断是否能将一副牌分成若干组,每组中的牌数量相同。
- 统计每种牌的数量:使用一个 Map 对象 count 存储每种牌的数量,其中键为牌的数字,值为对应牌的数量。
- 遍历可能的分组大小:使用一个循环从2到牌的总数 N,尝试不同的分组大小。
- 检查每种牌的数量是否能被分组大小 X 整除:在内部循环中,遍历每种牌的数量,检查是否能够被当前的分组大小 X 整除。
- 如果所有牌的数量都能被当前分组大小 X 整除,则返回 true,表示可以分组;否则,继续尝试下一个分组大小。
- 如果所有可能的分组大小都无法使所有牌被整除,则返回 false,表示无法分组。
- 这个暴力解法的思路相对简单直观,但它的效率可能受到牌的总数和每种牌的数量的影响,因为它需要尝试多个分组大小。
2 )方案 2: 最大公约数
function hasGroupsSizeX(deck: number[]): boolean {
// 边界情况
if (deck.length < 2) return false
// 对牌进行排序,无所谓顺序
deck.sort()
let flag: boolean = true // 最终的返回结果
let arr: number[][] = []
for (let i = 0, len = deck.length, tmp = []; i < len; i ++) {
tmp.push(deck[i])
// 基于当前的i逐个对比,判断是否和arr[i] 相同, 注意这个边界值
for (let j = i + 1; j <= len; j ++) {
// 相同时,逐一加入tmp进行存储
if (deck[i] === deck[j]) {
tmp.push(deck[i])
} else {
// 不相同时
// 数组长度不满足2的边界值处理
if (tmp.length < 2) {
return false
}
arr.push([].concat(tmp)) // 注意:这里数组是一个新的对象会被push到result中, 不能直接用tmp这个引用对象
tmp.length = 0 // 清空临时数组
i = j - 1 // 将i的下标移动到 j-1
break // 跳出此层循环
}
}
if (!flag) {
break
}
}
let result: number[] = arr.map(item => item.length) // 二维数据编程存储长度的一维数组
result = [...new Set(result)] // 对数组去重,减少重复运算
// 求是否是最大公约数函数
const isGcd = (arr: number[]) => {
// 所有的数组都是一个长度
if (arr.length === 1) {
return true
}
const gcd = (x: number,y: number) => !(x % y) ? y : gcd(y, x % y) // 求两个数的最大公约数
let final = 1 // 初始化最大公约数为1
while (arr.length > 1) {
let num1 = arr[0], num2 = arr[1];
final = gcd(num1, num2)
if(final === 1) {
return false
} else {
arr.splice(0,2, final) // // 迭代,继续求最大公约数
}
}
return final !== 1
}
return isGcd(result)
}
- 上述写法很长,基于正常的思维来处理的,两层循环进行聚类分组
- 之后简化数组存储,从存储原数据变更为存储同一数值类型的卡牌长度
- 然后,对所有数组进行去重操作,留下一个唯一的数组
- 在这个唯一数组中求最大公约数,如果最大公约数为1,则返回false, 否则返回true
- 需要注意的是:一些边界值的处理,有些情况开启边界值检测可以提前结束程序
- 这个程序的缺点是:复杂,维护性不佳
3 )方案 3: 优化版最大公约数
function hasGroupsSizeX(deck: number[]): boolean {
// 边界判断
if (deck.length < 2) return false
// 存储每张卡牌的总数
let result: number[] = []
// 临时存储对象
const tmp: object = {}
// 使用对象来存储累加当前数据的值
deck.forEach(item => {
tmp[item] = tmp[item] ? tmp[item] + 1 : 1
})
// 将最终的结果存放到一个数组中
for (let v of Object.values(tmp)) {
result.push(v)
}
// 对数组进行去重
result = [... new Set(result)]
// 求两个数的最大公约数
const gcd = (x: number,y: number) => !(x % y) ? y : gcd(y, x % y)
while (result.length > 1) {
const a = result.shift() // 移除当前数组第一个 并把返回值赋给 a
const b = result.shift() // 移除当前数组第一个 并把返回值赋给 b
// 得到当前最大公约数
const v = gcd(a, b)
if (v === 1) {
return false
} else {
result.unshift(v) // 将当前得到的新约数存入数组开头,进入下一个循环
}
}
return result.length ? result[0] > 1 : false
};
- 上述程序,通过修改数据结构,将处理数据的复杂度大大降低
- 注意下,上述记录同一数值的卡牌数量的object和while循环处理的精妙之处
Python3 版算法实现
1 )方案1: 暴力解法
class Solution:
def hasGroupsSizeX(self, deck: List[int]) -> bool:
# 创建一个字典,用于存储每种牌的数量
count = {}
# 统计每种牌的数量
for card in deck:
count[card] = count.get(card, 0) + 1
# 牌的总数
N = len(deck)
# 从2到N遍历,尝试不同的分组大小
for X in range(2, N + 1):
# 如果N可以被X整除,则有可能将牌分成X张一组
if N % X == 0:
# 检查每种牌的数量是否都能被X整除
all_divisible = all(val % X == 0 for val in count.values())
# 如果所有牌的数量都能被X整除,则可以分组
if all_divisible:
return True
# 所有可能的分组大小都无法分组,返回False
return False
2 )方案 2: 最大公约数
class Solution:
def hasGroupsSizeX(self, deck: List[int]) -> bool:
# 边界情况
if len(deck) < 2:
return False
# 对牌进行排序,无所谓顺序
deck.sort()
flag = True # 最终的返回结果
arr = []
i = 0
while i < len(deck):
tmp = [deck[i]]
# 基于当前的i逐个对比,判断是否和arr[i] 相同, 注意这个边界值
for j in range(i + 1, len(deck) + 1):
# 相同时,逐一加入tmp进行存储
if j < len(deck) and deck[i] == deck[j]:
tmp.append(deck[i])
else:
# 不相同时
# 数组长度不满足2的边界值处理
if len(tmp) < 2:
return False
arr.append(tmp.copy()) # 注意:这里数组是一个新的对象会被append到result中,不能直接用tmp这个引用对象
tmp.clear() # 清空临时数组
i = j - 1 # 将i的下标移动到 j-1
break # 跳出此层循环
if not flag:
break
i += 1
result = [len(item) for item in arr] # 二维数据编程存储长度的一维数组
result = list(set(result)) # 对数组去重,减少重复运算
# 求是否是最大公约数函数
def is_gcd(arr):
# 所有的数组都是一个长度
if len(arr) == 1:
return True
def gcd(x, y):
return y if x % y == 0 else gcd(y, x % y) # 求两个数的最大公约数
final = 1 # 初始化最大公约数为1
while len(arr) > 1:
num1, num2 = arr[0], arr[1]
final = gcd(num1, num2)
if final == 1:
return False
else:
arr.pop(0)
arr.pop(0)
arr.insert(0, final) # 迭代,继续求最大公约数
return final != 1
return is_gcd(result)
3 ) 方案 3: 优化版最大公约数
class Solution:
def hasGroupsSizeX(self, deck: List[int]) -> bool:
# 边界判断
if len(deck) < 2:
return False
# 存储每张卡牌的总数
result = []
# 临时存储对象
tmp = {}
# 使用字典来存储累加当前数据的值
for item in deck:
tmp[item] = tmp.get(item, 0) + 1
# 将最终的结果存放到一个列表中
for v in tmp.values():
result.append(v)
# 对列表进行去重
result = list(set(result))
# 求两个数的最大公约数
def gcd(x, y):
return y if x % y == 0 else gcd(y, x % y)
while len(result) > 1:
a = result.pop(0) # 移除当前列表第一个并将其返回给a
b = result.pop(0) # 移除当前列表第一个并将其返回给b
# 得到当前最大公约数
v = gcd(a, b)
if v == 1:
return False
else:
result.insert(0, v) # 将当前得到的新约数插入列表开头,进入下一个循环
return result[0] > 1 if result else False
Golang 版算法实现
1 )方案1: 暴力解法
func hasGroupsSizeX(deck []int) bool {
// 创建一个map,用于存储每种牌的数量
count := make(map[int]int)
// 统计每种牌的数量
for _, card := range deck {
count[card]++
}
// 牌的总数
N := len(deck)
// 从2到N遍历,尝试不同的分组大小
for X := 2; X <= N; X++ {
// 如果N可以被X整除,则有可能将牌分成X张一组
if N%X == 0 {
// 检查每种牌的数量是否都能被X整除
allDivisible := true
for _, val := range count {
if val%X != 0 {
// 如果有任何一种牌的数量不能被X整除,则无法分组
allDivisible = false
break
}
}
// 如果所有牌的数量都能被X整除,则可以分组
if allDivisible {
return true
}
}
}
// 所有可能的分组大小都无法分组,返回false
return false
}
2 )方案 2: 最大公约数
import (
"sort"
)
func hasGroupsSizeX(deck []int) bool {
// 边界情况
if len(deck) < 2 {
return false
}
// 对牌进行排序,无所谓顺序
sort.Ints(deck)
flag := true // 最终的返回结果
var arr [][]int
i := 0
for i < len(deck) {
tmp := []int{deck[i]}
// 基于当前的i逐个对比,判断是否和arr[i] 相同, 注意这个边界值
for j := i + 1; j <= len(deck); j++ {
// 相同时,逐一加入tmp进行存储
if j < len(deck) && deck[i] == deck[j] {
tmp = append(tmp, deck[i])
} else {
// 不相同时
// 数组长度不满足2的边界值处理
if len(tmp) < 2 {
return false
}
arr = append(arr, append([]int(nil), tmp...)) // 注意:这里数组是一个新的对象会被append到result中,不能直接用tmp这个引用对象
tmp = nil // 清空临时数组
i = j - 1 // 将i的下标移动到 j-1
break // 跳出此层循环
}
}
if !flag {
break
}
i++
}
result := make([]int, len(arr))
for i, item := range arr {
result[i] = len(item)
}
// 求是否是最大公约数函数
isGcd := func(arr []int) bool {
// 所有的数组都是一个长度
if len(arr) == 1 {
return true
}
// 求两个数的最大公约数
gcd := func(x, y int) int {
for y != 0 {
x, y = y, x%y
}
return x
}
final := 1 // 初始化最大公约数为1
for len(arr) > 1 {
num1, num2 := arr[0], arr[1]
final = gcd(num1, num2)
if final == 1 {
return false
} else {
arr = append(arr[:0], arr[2:]...) // 迭代,继续求最大公约数
arr = append(arr, final)
}
}
return final != 1
}
return isGcd(result)
}
3 ) 方案 3: 优化版最大公约数
func hasGroupsSizeX(deck []int) bool {
// 边界判断
if len(deck) < 2 {
return false
}
// 存储每张卡牌的总数
tmp := make(map[int]int)
for _, item := range deck {
tmp[item]++
}
// 将最终的结果存放到一个切片中
result := []int{}
for _, v := range tmp {
result = append(result, v)
}
// 对切片进行去重
seen := make(map[int]bool)
j := 0
for _, v := range result {
if _, ok := seen[v]; !ok {
seen[v] = true
result[j] = v
j++
}
}
result = result[:j]
// 求最大公约数
for len(result) > 1 {
a := result[0]
b := result[1]
result = result[2:]
v := gcd(a, b)
if v == 1 {
return false
}
result = append([]int{v}, result...)
}
return result[0] > 1
}
// 辅助函数:求两个数的最大公约数
func gcd(x, y int) int {
if y == 0 {
return x
}
return gcd(y, x%y)
}
1883



被折叠的 条评论
为什么被折叠?



