前言

本博文部分图片, 思路来自于剑指offer 或者编程珠玑

问题描述

23 判断扑克牌的顺子_算法

思路

对于这个问题, 书中给出了一种思路 [大小鬼使用数字0来表示]

思路一 : 先将数组进行排序, 然后获取到第一个非0元素的位置, 然后获取之后的元素的每两个相邻元素之间的差值[“空白元素部分”], 相邻两个元素相同为剪枝条件[根据题目的需求], 可以直接返回false, 统计所有的”空白元素”的个数, 最后比较总的”空白元素”的个数 和数组前面”0的个数”, 如果“空白元素”个数大于”0的个数”, 返回false[通配符都填充不满]


后面两种思路是我的
思路一 : 其实 和上面书中的思路基本上一致, 不过 我这里换了一种方式来统计, 使用的是标志数组

思路二 : 直接使用逻辑判定, 令”最大元素 和第一个非0元素的差值”为deltaVal, “最大元素的索引 和第一个非0元素的索引差值”为deltaIdx
    如果 delta大于了arr.length 表示就算吧所有的”0元素”都去补充空白, 仍然会存在空白, 返回false
    如果 deltaVal小于deltaIdx 表示”最大元素 和第一个非0元素”之间存在重复的数据, 返回false
    其他场景返回true

参考代码

/**
 * file name : Test19IsContinuousSeq.java
 * created at : 3:04:00 PM Jun 9, 2015
 * created by
 */

package com.hx.test05;

public class Test19IsContinuousSeq {

    // 可以表示任意数字
    static int ALL_MATCH = 0;

    // 判断给定的序列是否是连续的   0可以表示任意数
    public static void main(String []args) {

        int[] arr = new int[] {3, 5, 4, 7 };

        isContinuousSeq(arr);
        isContinuousSeq02(arr);
    }

    // 思路 : 先将arr排序, 获取第一个非0的数字的索引
        // 获取最大的数和第一个非0数之差, 获取最大数的索引和第一个非0数索引之差
        // 先进行剪枝, 如果最大的差值大于了(索引差值+0的个数), 或者差值小于索引的差值, 则视为不可能连续, 直接返回false, 
    // 然后  利用一个长度为(deltaVal+1)的辅助数组  空间, 来记录那些数据存在    如果数组中空缺的数字个数大于了0的个数, 则视为, 即使使用了所有的0, 任然会存在空缺, 返回false
        // 否则  返回true
    public static void isContinuousSeq(int[] arr) {
        Arrays.sort(arr);
        int firstNonZeroIdx = getFirstNonAllMatchIdx(arr);

        int deltaVal = arr[arr.length-1] - arr[firstNonZeroIdx];
        int deltaIdx = arr.length-1 - firstNonZeroIdx;

        // 数据的值相差太大的, 用0都不可能弥补的, 或者相差太小, 存在一个或多个一样的
        if((deltaVal > deltaIdx + firstNonZeroIdx) || (deltaVal < deltaIdx) ) {
            Log.log(false);
            return ;
        }

        // 记录数据的存在情况
        boolean[] isExists = new boolean[deltaVal + 1];
        int falseNum = isExists.length;
        boolean isContinuous = false;
        for(int i=firstNonZeroIdx; i<arr.length; i++) {
            if(arr[i] != ALL_MATCH) {
                if(!isExists[arr[i] - arr[firstNonZeroIdx]]) {
                    isExists[arr[i] - arr[firstNonZeroIdx]] = true;
                    falseNum --;
                }
            }
        }

        isContinuous = falseNum <= firstNonZeroIdx;

        Log.log(isContinuous);
    }

    // 在初始剪枝条件之后, 只需要判断是否存在重复的元素就好了
    public static void isContinuousSeq02(int[] arr) {
        Arrays.sort(arr);
        int firstNonZeroIdx = getFirstNonAllMatchIdx(arr);

        int deltaVal = arr[arr.length-1] - arr[firstNonZeroIdx];
        int deltaIdx = arr.length-1 - firstNonZeroIdx;

        // 数据的值相差太大的, 用0都不可能弥补的, 或者相差太小, 存在一个或多个一样的
        if((deltaVal > deltaIdx + firstNonZeroIdx) || (deltaVal < deltaIdx) ) {
            Log.log(false);
            return ;
        }

        Log.log(true);
    }

    // 获取arr中第一个非0 的数据的索引
    private static int getFirstNonAllMatchIdx(int[] arr) {
        int idx = -1;
        for(int i=0; i<arr.length; i++) {
            if(arr[i] != ALL_MATCH) {
                idx = i;
                break ;
            }
        }

        return idx;
    }

}

效果截图

23 判断扑克牌的顺子_算法_02

总结

对于排序的操作, 因为差值范围不大, 可以采用O(n)的桶排序
查找非0元素的操作, 因为是有序嘛, 可以采用二分查找的思路针对当前场景进行改写

注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!