剑指offer-扑克牌顺子

题目描述

  • LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…..LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何。为了方便起见,你可以认为大小王是0。
  • 地址: 牛客链接

问题分析

  • 排序法:先将数组排序,那么0肯定在前,其他元素在后。然后遍历数组,累加记录 0的数目,看 0 的数目能够弥补当前两个元素的差距(如4,6之间的差距便是 1,需要 一个 0作为 5 去弥补)。只有当遍历的过程中,0的数目一直能弥补非0元素之间的差距,并且非0元素不会出现重复时,才能形成顺子。
    时间复杂度:O(NlogN)
  • 宏观法:不拘泥与小的细节,不去考虑具体 0的数目,从宏观上考虑问题,如果这个数组的 max 与min 相差大于4,那么肯定不会成为顺子。如果出现了非0的重复元素,那么肯定不是顺子。
    所以,如果要是顺子必须满足:

    • max - min < 5
    • 非0元素不为重复
      所以,遍历数组一次便可以得到答案

    那么,根据检验重复的方法,又可以有以下三种做法:

    • set检验重复
    • count数组检验重复(count[2] = 1 便表示 2 出现了 1 次)
    • bitmap 的思想,用一位来检验重复,用一个flag,二进制表现形式为32位,第 i 位便可以表示 i 是否出现过。若没有,则为1,若出现了,则置1。这样比以上更加节省空间。

    时间复杂度:O(N)

经验教训

  • bitmap 检验重复的方法
    • 检验第 i 位 是否为 1用 & 与 >> ((flag >> i) & 1) == 1
    • 将第 i 位设置为 1,其他位不变:用 | 与 << flag |= (1 << i);
  • 宏观考虑问题

代码实现

  • 排序法:
    public boolean isContinuous(int [] numbers) {
        if (numbers == null || numbers.length != 5) {
            return false;
        }
        Arrays.sort(numbers);
        int numOfZero = 0;
        for (int i = 0; i < numbers.length; i++) {
            if (numbers[i] == 0) {
                numOfZero++;
                continue;
            }else {
                //出现重复
                if (i - 1 >= 0 && numbers[i - 1] == numbers[i]) {
                    return false;
                }
                if (i - 1 >= 0 && numbers[i - 1] != 0) {
                    //计算差距
                    int numOfInterval = numbers[i] - numbers[i - 1] - 1;
                    //当前0是否能弥补
                    if (numOfInterval > numOfZero) {
                        //不能
                        return false;
                    }
                    //弥补后更新0的数量
                    numOfZero -= numOfInterval;
                }
            }
        }
        return true;
    }
  • count数组检验重复
    public boolean isContinuous(int [] numbers) {
        if (numbers == null || numbers.length != 5) {
            return false;
        }
        int max = -1;
        int min = 14;
        int[] count = new int[14];
        for (int i = 0; i < 5; i++) {
            if (numbers[i] == 0) {
                continue;
            }
            if (count[numbers[i]] == 0) {
                //未出现重复,更新count,max,min
                count[numbers[i]]++;
                max = max >= numbers[i] ? max : numbers[i];
                min = min <= numbers[i] ? min : numbers[i];
                if (max - min > 4) {
                    return false;
                }
            }else {
                //出现重复
                return false;
            }
        }
        return true;
    }
  • bitmap检验重复
    public boolean isContinuous(int [] numbers) {
        if (numbers == null || numbers.length != 5) {
            return false;
        }
        int max = -1;
        int min = 14;
        int flag = 0;
        for (int i = 0; i < 5; i++) {
            if (numbers[i] == 0) {
                continue;
            }
            //出现重复
            if (((flag >> numbers[i]) & 1) == 1) {
                return false;
            }else {
                //未出现重复,将相应位置1
                flag |= (1 << numbers[i]);
                //更新 max,min
                max = max >= numbers[i] ? max : numbers[i];
                min = min <= numbers[i] ? min : numbers[i];
                if (max - min > 4) {
                    return false;
                }
            }
        }
        return true;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值