题目:扑克牌顺子
现在有2副扑克牌,从扑克牌中随机五张扑克牌,我们需要来判断一下是不是顺子。
有如下规则:
1. A为1,J为11,Q为12,K为13,A不能视为14
2.大、小王为0,0可以看作任意牌
3.如果给出的五张牌能组成顺子(即这五张牌是连续的)就输出true,否则就输出false。
例如:给出数据[6,0,2,0,4]
中间的两个0一个看作3,一个看作5。即:[6,3,2,5,4]
这样这五张牌在[2,6]区间连续,输出true
数据保证每组5个数字,每组最多含有4个零,数组的数取值为[0, 13]
思路分析:在读题目的同时,我们应该去思考在什么情况下,扑克牌的五张牌不能构成顺子,同时也应该去思考解题的方法与技巧。
解法一:首先判断扑克牌要进行排序的张数,是否符合题目要求,如果是不符合题目要求的张数,可以直接返回false。在符合题目要求的同时,我们可以将扑克牌中的大小王(任意张)的个数计算出来,将扑克牌的顺序按大小排序完整后,进行下一轮的问题思考。
其次,可以想象一个问题,在两副牌中有四个大小王,所以可以分析如下情况:
当五张牌只有一张王的时候,按照大小顺序排序后,王应该在最前面,剩下的四张牌的大小其中相邻两张之间不能超过2,而且只能存在一次这种情况的发生,如果超过2,则直接返回false,例如0,1,4,5,6,如果没有超过,则返回true。
以此类推,有两张王的时候,剩下的四张牌的大小相邻两张牌之间不能超过3,或者有相邻两个两张牌之间的差距不能超过1,以此类推,就发现了解决问题的关键。
例子具体分析如下所示:
6 | 0 | 2 | 0 | 4 |
首先,判断0的个数为2,按顺序排放如下 | ||||
0 | 0 | 2 | 4 | 6 |
从数字4的下标开始判断相邻值的大小,将相邻值的大小累加后得到一个值 | ||||
0 | 0 | 2 | 4 | 6 |
根据判断可得,4-2-1 = 1,6-2-1 = 1,将两个值的大小相加后,与0的个数做比较:
当0的个数大于两个值的和时,返回true。
当0的个数小于两个值的和时,返回false。
当0的个数等于两个值的和时,返回true,由这几条可知该实例最后返回的结果为true。
其中C++代码如下所示:
class Solution { public: bool IsContinuous( vector<int> numbers ) { if(numbers.size() != 5) return false; sort(numbers.begin(), numbers.end()); int numzero = 0; for(int i = 0; i != 5;i ++){//判断0的个数 if(!numbers[i]) numzero++; } int target = numzero + 1;//下标数 int numgap = 0; while(target < 5){ if(numbers[target - 1] == numbers[target])//像对子这种意外情况的发生 return false; numgap += numbers[target] - numbers[target - 1] - 1; target++; } return (numzero >= numgap) ? true : false; } };
时间复杂度为O(N)。
解法2:我们通过观察可以发现一个常识性的问题就是,因为是顺子,所以其最大值和最小值的差值不会超过5(不能允许有对的情况出现)。
以上述例子为例:
6 | 0 | 2 | 0 | 4 |
首先,判断0的个数为2,按顺序排放如下 | ||||
0 | 0 | 2 | 4 | 6 |
判断除0以外的最大值和最小值之间的差距 | ||||
6-2<5,则直接返回true |
该方法C++代码如下所示:
class Solution { public: bool IsContinuous( vector<int> numbers ) { int numzero = 0; sort(numbers.begin(), numbers.end()); for(int i = 0; i < 5;i++){ if(numbers[i] == 0) numzero++; else if(numbers[i] == numbers[i+1]) return false; } return numbers[4] - numbers[numzero] < 5; } };这种方法属于按照常识性问题分析的办法,可以将一个复杂的问题化解成简单的东西,从而得出准确的答案。