卡牌分组
- 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 <= 1 0 4 10^4 104
- 0 <= deck[i] < 1 0 4 10^4 104
算法实现
1 )方案 1文章来源:https://www.toymoban.com/news/detail-430682.html
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
- 需要注意的是:一些边界值的处理,有些情况开启边界值检测可以提前结束程序
- 这个程序的缺点是:复杂,维护性不佳
2 )方案 2文章来源地址https://www.toymoban.com/news/detail-430682.html
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循环处理的精妙之处
到了这里,关于数据结构与算法之数组: Leetcode 914. 卡牌分组 (Typescript版)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!