【动漫算法】扑克牌的顺子

题目

从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。

示例 1:

输入: [1,2,3,4,5]
输出: True

示例 2:

输入: [0,0,1,2,5]
输出: True

限制:

数组长度为 5

数组的数取值为 [0, 13] .

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/bu-ke-pai-zhong-de-shun-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

本题目中的顺子,只是代表从1开始的到13, 和现实玩牌的顺子有一点差别的就是,10 J Q K A 不构成本题目中的顺子:

思路

如果5张牌中无大小王

如果要构成是顺子的情况,那么必然需要连起来,比如像下图中的 1,2,3,4,5.


由于必须构成顺子,所以最大值5减去最小值1等于4,如果差值大于4,那么必然不是顺子了。

如果 5 张牌中有1个大小王

有一个大小王,也就是有一个数字 0,对于我们举的例子1,2,3,4,5,中有一个数字被 0进行替换了。

如果想要构成顺子,那么必须是1,2,3,4,5中的任意一个被0替换了,因为想要0可以变成任意的一个数字

1 被替换

除了0以为,最大差值5-2是3.

2 被替换

除了0以为,最大差值5-1是4.

3 被替换

除了0以为,最大差值5-1是4.

4 被替换

除了0以为,最大差值5-1是4.

5 被替换

除了0以为,最大差值4-1是3.

上述5种情况,都是可以构成顺子的情况。

从上述 5 种情况可以看出来,只要是能构成顺子的情况,除了0以为,那么最大值和最小值差值必然小于等于4

如果 5 张牌中有2个大小王










总共有上述10种情况,观察这10种情况知道:

除了0以为,如果可以构成顺子,最大值,最小值的差必然小于等于 4.

思路总结

从上面的流程中可以知道本题的算法思路:

  • 遍历5张牌,遇到是0的话直接跳过
  • 遍历的时候有集合set把数存在里面,如果有重复,那么必然有对子,有对子必然无法变成顺子


  • 用一个max 存最大值,有一个 min 存最小值,如果最后max和mix的差值小于等于4,那么说明是顺子。

动画

动画
.

代码·

Python

class Solution:
    def isStraight(self, nums: List[int]) -> bool:
        repeat = set()
        ma, mi = 0, 14
        for num in nums:
            if num == 0: continue # 跳过大小王
            ma = max(ma, num) # 最大牌
            mi = min(mi, num) # 最小牌
            if num in repeat: return False # 若有重复,提前返回 false
            repeat.add(num) # 添加牌至 Set
        return ma - mi < 5 # 最大牌 - 最小牌 < 5 则可构成顺子 

Java

class Solution {
    public boolean isStraight(int[] nums) {
        Set<Integer> repeat = new HashSet<>();
        int max = 0, min = 14;
        for(int num : nums) {
            if(num == 0) continue; // 跳过大小王
            max = Math.max(max, num); // 最大牌
            min = Math.min(min, num); // 最小牌
            if(repeat.contains(num)) return false; // 若有重复,提前返回 false
            repeat.add(num); // 添加此牌至 Set
        }
        return max - min < 5; // 最大牌 - 最小牌 < 5 则可构成顺子
    }
}

C++

class Solution {
public:
    bool isStraight(vector<int>& nums) {
        // bool型的哈希表,记录每张扑克牌是否出现过
        // 出现过为true,否则为false
        vector<bool> record(15, false);
        // minValue和maxValue分别记录摸到的5张牌中的最小值和最大值
        // 5张牌要成为顺子的条件就是maxValue-minValue+1 <= 5
        // 等于5正好是顺子,小于5则可以用大小王填补空缺进而连成顺子
        int minValue = 14, maxValue = 0;
        // 遍历数组
        for (int n : nums) {
            // 碰到大小王,直接进行下一轮循环
            if (n == 0) continue;
            // 发现重复的扑克牌,则判定不是顺子
            if (record[n] != false) return false;
            // 在哈希表中记录当前遍历到的扑克牌为已出现
            record[n] = true;
            // 更新5张牌中的最大值和最小值
            minValue = min(minValue, n);
            maxValue = max(maxValue, n);
        }
        // 判断是否满足成为顺子的条件
        return maxValue - minValue + 1 <= 5;
    }
};

JS

/**
 * @param {number[]} nums
 * @return {boolean}
 */
var isStraight = function(nums) {
    /* 
       分治思想 五张牌构成顺子的充分条件需要满足
       1. 不重复 使用Set去重
       2. max - min < 5 最大牌值 减去 最小牌值 小于5 且跳过大小王
    */
    const set = new Set()
    let min = 14, max = 0 // ⚠️ min和max的初始值是两个边界值[0, 13]

    for (const num of nums) {
        // 遇到大小王 跳过
        if (!num) continue
        // 遇到重复则直接 返回false
        if (set.has(num)) return false
        set.add(num)
        // 迭代更新 min和max 以及set
        min = Math.min(min, num)
        max = Math.max(max, num)
    }

    return max - min < 5

};

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容