数据结构与算法之数组: Leetcode 914. 卡牌分组 (Typescript版)

卡牌分组

  • 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

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

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循环处理的精妙之处
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>