扑克牌顺子判断

判断5张扑克牌是否为顺子,大小王可作为任意牌

问题

从扑克牌中随机抽取 5 张牌,判断是否为顺子(5 张牌连续)。其中 A 为 1,J 为 11,Q 为 12,K 为 13,大小王用 0 表示,可以替代任意数字。

示例:

  • [1, 2, 3, 4, 5]true
  • [0, 0, 1, 2, 5]true(大小王补 3、4)
  • [1, 2, 3, 4, 4]false(有对子)

解答

思路

顺子的条件:

  1. 除 0 外没有重复的牌
  2. 最大值 - 最小值 < 5(0 不参与计算)

实现

/**
 * 判断5张牌是否为顺子
 * @param {number[]} nums - 5张牌,0表示大小王
 * @return {boolean}
 */
function isStraight(nums) {
  // 用 Set 检测重复
  const set = new Set();
  let max = 0;
  let min = 14; // 牌最大是13

  for (const num of nums) {
    // 跳过大小王
    if (num === 0) continue;

    // 有重复牌,不可能是顺子
    if (set.has(num)) return false;
    set.add(num);

    // 更新最大最小值
    max = Math.max(max, num);
    min = Math.min(min, num);
  }

  // 最大值和最小值差距小于5才能组成顺子
  return max - min < 5;
}

测试

console.log(isStraight([1, 2, 3, 4, 5]));   // true
console.log(isStraight([0, 0, 1, 2, 5]));   // true
console.log(isStraight([0, 0, 2, 2, 5]));   // false(有对子)
console.log(isStraight([11, 0, 9, 0, 8]));  // true(8,9,10,11,12)
console.log(isStraight([1, 2, 12, 13, 0])); // false(差距太大)

排序解法

function isStraight(nums) {
  // 排序
  nums.sort((a, b) => a - b);
  
  let jokers = 0; // 大小王数量
  
  for (let i = 0; i < 4; i++) {
    if (nums[i] === 0) {
      jokers++;
      continue;
    }
    // 有对子
    if (nums[i] === nums[i + 1]) return false;
  }
  
  // 最大牌 - 最小非零牌 < 5
  return nums[4] - nums[jokers] < 5;
}

关键点

  • 顺子两个条件:无重复 + 极差小于 5
  • 大小王(0)不参与重复检测和极差计算
  • Set 解法时间复杂度 O(n),排序解法 O(n log n)
  • 不需要真的去”填补”空缺,只需验证条件