一、题目
题目链接:力扣
从若干副扑克牌中随机抽 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] .
二、题解
1、思路
- 除大小王外,所有牌 无重复 ;
- 设此 5 张牌中最大的牌为 max ,最小的牌为 min (大小王除外),则需满足:max - min < 5
满足上述两个条件即可组成顺子!
为什么呢?
无重复很好理解,为什么需要满足:max - min < 5呢?
首先假设满足不重复的条件,则有:
- 假设不存在大王小王,也就是不存在0,直接max-min=x,那么x一定有,x>=4,什么情况一定是顺子呢?x=4,只有是4的时候才能保证是连续的;
- 假设存在大王小王,也就是存在0,max-min=x,那么x一定有,x>=0,什么情况一定是顺子呢?max-min<=4。因为万能的0可以变成任何一个数字进行填充,如果max-min很小,那么0不仅可以填充min~max之间的数字,甚至可以填充到min~max范围外部,但是如果max-min>4,0甚至无法填充min~max之间的数字。
- 具体来说:0的个数可能存在0~5个,我们一一分析。
- 注意:max-min最大的时候是0填充在内部的时候,最小的时候是0填充在外部(即非零数字连续)的时候。
- 0有1个,3<=max-min<=4;
- 0有2个,2<=max-min<=4;
- 0有3个,1<=max-min<=4;
- 综上,max-min<5。
2、代码实现
🎯 哈希表 + 最值差
class Solution {
public:
bool isStraight(vector<int>& nums) {
unordered_set<int> set;
int max_num = INT_MIN;
int min_num = INT_MAX;
for(auto& num : nums)
{
if(num == 0)continue;
else
{
// 统计最值
max_num = max(max_num, num);
min_num = min(min_num, num);
if(set.find(num) != set.end())return false;// 重复,不能组成顺子
else set.insert(num);// 无重复加入哈希表
}
}
// 最大牌 - 最小牌 < 5 则可构成顺子
return max_num - min_num < 5;
}
};
🎯 排序 + 重复排除
易错点:
- for循环中一定要在nums[i] != 0的时候才能和后面的对比!
- 会不会少统计一个0?如果nums中存在4个及以下的0,下面的代码肯定没有问题,很好理解;如果存在5个0,肯定可以组成顺子,下面的代码可以完成判断嘛?nums[4] - nums[count_0] = 0 - 0 < 5,可以判断。
class Solution {
public:
bool isStraight(vector<int>& nums) {
int n = nums.size();
int num_0 = 0;
sort(nums.begin(), nums.end());
for(int i = 0; i < n - 1; i++)
{
if(nums[i] == 0)num_0++;
else if(nums[i] == nums[i+1])return false;
}
// num_0可以取值为0 1 2 3 4
// nums[num_0]刚好是第一个不为0的数字(下标和实际数量差1)
// num_0 = 0 1 2 3; 符合max - min < 5
// num_0 = 4; 是顺子,下面的判断语句是nums[4]-nums[4],小于5,true
// num_0 = 5; 是顺子,下面的判断语句是nums[4]-nums[4],num_0=4哦!小于5,true
// num_0=4和5的时候都是顺子,因此此处设计成nums[4]-nums[4],刚好。
return nums[n - 1] - nums[num_0] < 5;
}
};
3、复杂度分析
🎯 哈希表 + 最值差
时间复杂度:O(n)=O(5)=O(1);
空间复杂度:O(n)=O(5)=O(1)。
🎯 排序 + 重复排除
时间复杂度:O(nlogn)=O(5log5)=O(1);
空间复杂度:O(1)。
4、运行结果
🎯 哈希表 + 最值差
🎯 排序 + 重复排除