• 剑指offer(C++)-JZ61:扑克牌顺子(算法-模拟)


    作者:翟天保Steven
    版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

    题目描述:

    现在有2副扑克牌,从扑克牌中随机五张扑克牌,我们需要来判断一下是不是顺子。
    有如下规则:
    1. A为1,J为11,Q为12,K为13,A不能视为14
    2. 大、小王为 0,0可以看作任意牌
    3. 如果给出的五张牌能组成顺子(即这五张牌是连续的)就输出true,否则就输出false。
    4.数据保证每组5个数字,每组最多含有4个零,数组的数取值为 [0, 13]

    要求:空间复杂度 O(1),时间复杂度O(nlogn),本题也有时间复杂度O(n) 的解法

    示例:

    输入:

    [6,0,2,0,4]
    

    返回值:

    true
    

    说明:

    中间的两个0一个看作3,一个看作5 。即:[6,3,2,5,4]
    这样这五张牌在[2,6]区间连续,输出true 

    解题思路:

    本题考察算法场景模拟。两种解题思路。

    1)排序法

           将卡牌排序,复杂度O(nlogn),遍历一次O(n),记录0的数量,如果出现重复数据则中断返回false,对非0非重复数据进行间隔累加。如果最终0的数量大于等于间隔累加数,则说明可以形成顺子。

           比如2 0 4 0 0 7,排序后0 0 0 2 4 7,2和4间隔1,4和7间隔2,三个0刚好补齐,多余的0可以在前后凑顺子。

          如果0的数量和输入数量一致,则无法形成顺子。

          总的复杂度O(nlogn+n),即O(nlogn)。

    2)查找法

           进行双层循环。发现0则跳过;通过set的count函数检测是否遇到重复,重复则返回false;无问题则插入数值,同时刷新最值。

           如果能持续到循环结束,此时最大值减去最小值的差值小于5,则一定能构成顺子,反之则不行。

           时间复杂度O(n2),遍历为n,查找为n,即n*n。

    3)查找法进阶

           与查找法流程基本一致,区别在于检测重复值。因为卡牌最多只有0-14这15个数字,我们充分利用位运算特性,如果遇到某个数值,比如5,则将bm的第五位设为1,即0001 0000,每次遇到一个新数值就刷新bm。

           这样可以用&运算检测是否重复,只有某一位都为1,才会触发重复标识,进而返回false。用|运算刷新bm。

           时间复杂度O(n),遍历为n,查找为1,即n*1。

    测试代码:

    1)排序法

    1. class Solution {
    2. public:
    3. // 是否顺子
    4. bool IsContinuous(vector<int>& numbers) {
    5. // 排序法
    6. // 时间复杂度O(nlogn+n)=O(nlogn)
    7. // 快速排序
    8. sort(numbers.begin(), numbers.end());
    9. int size = int(numbers.size());
    10. int zeroNum = 0;
    11. int diff = 0;
    12. for(int i = 0; i < size; ++i){
    13. // 记录0的数量
    14. if(numbers[i] == 0){
    15. zeroNum++;
    16. }
    17. // 判断是否有重复数据
    18. else if(i > 0 && numbers[i - 1] == numbers[i]){
    19. return false;
    20. }
    21. // 累加间隔长度,注意0不参与间隔计算
    22. else if(i > 0 && numbers[i - 1] != 0){
    23. // 比如23之间无多余间隔,则diff累加024之间有3这一个间隔,diff累加1
    24. diff += numbers[i] - numbers[i - 1] - 1;
    25. }
    26. }
    27. // 0的数量只有大于等于多余的间隔长度,才能凑成顺子
    28. if(zeroNum == size){
    29. return false;
    30. }
    31. else if(zeroNum >= diff){
    32. return true;
    33. }
    34. else{
    35. return false;
    36. }
    37. }
    38. };

    2)查找法

    1. class Solution {
    2. public:
    3. // 是否顺子
    4. bool IsContinuous(vector<int>& numbers) {
    5. // 查找法
    6. // 时间复杂度O(n2)
    7. int size = int(numbers.size());
    8. set<int> s;
    9. int minNum = 13;
    10. int maxNum = 0;
    11. for(int i = 0; i < size; ++i){
    12. // 跳过0
    13. if(numbers[i] == 0){
    14. continue;
    15. }
    16. // 检查重复值,复杂度O(n)
    17. if(s.count(numbers[i])){
    18. return false;
    19. }
    20. // 插入
    21. s.insert(numbers[i]);
    22. // 刷新最值
    23. minNum = min(minNum, numbers[i]);
    24. maxNum = max(maxNum, numbers[i]);
    25. }
    26. // 最大值和最小值之差小于5,则一定能构成顺子
    27. bool result = (maxNum - minNum) < 5;
    28. return result;
    29. }
    30. };

    3)查找法进阶

    1. class Solution {
    2. public:
    3. // 是否顺子
    4. bool IsContinuous(vector<int>& numbers) {
    5. // 查找法进阶
    6. // 时间复杂度O(n)
    7. int size = int(numbers.size());
    8. int bm = 0;
    9. int minNum = 13;
    10. int maxNum = 0;
    11. for(int i = 0; i < size; ++i){
    12. // 跳过0
    13. if(numbers[i] == 0){
    14. continue;
    15. }
    16. // 检查重复值,复杂度O(1)
    17. if((bm & (1 << numbers[i])) != 0){
    18. return false;
    19. }
    20. // 数字对应位改为1
    21. bm |= (1 << numbers[i]);
    22. // 刷新最值
    23. minNum = min(minNum, numbers[i]);
    24. maxNum = max(maxNum, numbers[i]);
    25. }
    26. // 最大值和最小值之差小于5,则一定能构成顺子
    27. bool result = (maxNum - minNum) < 5;
    28. return result;
    29. }
    30. };

  • 相关阅读:
    学生Dreamweaver静态网页设计 基于HTML+CSS+JavaScript制作简食餐厅美食网站制作
    Vue3异步组件和Suspense
    低代码维格云明细视图入门教程
    [机器学习]深度学习初学者大疑问之nn.Linear(a,b)到底代表什么?
    【微信小程序AR】基于Kivicube零代码实现微信小程序AR
    notepad 文本筛选并替换
    Win10电脑重装系统更新关闭了还自动打开怎么解决?
    Python自动化测试的2种思路
    【Elasticsearch教程16】Mapping字段类型之join
    如何使用PHP进行数据库连接和操作?
  • 原文地址:https://blog.csdn.net/zhaitianbao/article/details/132694103