算法题:由一道简单的取余数面试题推导类似题目解法

326 阅读1分钟

这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

最近碰到一个面试题目,比较简单,基本上一看就能想出一种解法,不过真的这么简单吗? 我们先来看看这道题目:

给定整数m,n 统计二者之间可以被2和3整除的整数的数量(包括m,n)
0<m<=n<=10的12次方

这道题很简单,只要扫一眼就能得出第一种解法
解法一

private static long remainderCount1(long m,long n){
    long count = 0;
    for (long i = m; i <= n; i++) {
        if (i % 2 == 0 && i % 3 == 0){
            count++;
        }
    }
    return count;
}

这种解法时间复杂度为O(n),空间复杂度为O(1)。 但如果经过仔细的思考,发现这种题目可以通过数学的方法来降低时间复杂度,类似于高斯算法,将“1+2+3+4+5+···+(n-1)+n”这样的计算提炼出公式,改为以首项加末项乘以项数除以2用来计算,也就是n*(n+1)/2

那么这道题能否也转成公式呢?

我们先来看看,能被2和3整除的数有什么特点,它的特点就是能被2和3的最小公倍数整除

那么此时,我们可以计算2和3的最小公倍数为6,那么我们现在就可以从所给数字中寻找所有6的倍数了,那么这该如何寻找呢?

很简单,直接除以6,就能得知里面有多少个数字能6整除,但这里还要注意一点,就是,如何处理数字的边界问题。为了方便我们来举例说明。当m=10,n=23时,里面只有两个数12,18那么,我们可以抛弃掉12之前的数字,将前面的指针向前推进到第一个符合要求的数字即可,定义一个起始数值为pre = m + (6 - m % 6);而后面的n,我们同样,只需要向前退到数列中最后一个符合条件的数字即可,suf = n + (6 - n % 6)。由此可得出解法二。

解法二:

private static long remainderCount2(long m, long n) {
    long pre, suf;
    //计算范围内第一个符合的数
    if (m % 6 == 0) {
        pre = m;
    } else {
        pre = m + (6 - m % 6);
    }
    //计算范围内最后一个符合的数
    if (n % 6 == 0) {
        suf = n;
    } else {
        suf = n - (n % 6);
    }

    //计算符合的个数
    long count = (suf - pre) / 6 + 1;
    return count;
}

解法二的解法时间复杂度为O(n),空间复杂度为O(1)。

那么我们能不能通过解法二,将“能被2和3整除的数”推出“能被x和y整除的数”这种更广义的范围呢?

当然可以,我们只需要先求出x和y的最小公倍数就行了。

给定整数m,n 统计二者之间可以被x和y整除的整数的数量(包括m,n)
0<m<=n<=10的12次方 直接套用模板


private static long remainderCount2(long m, long n,int x,int y) {
    long pre, suf;
    int lcm = leastCommonMultiple(x,y);
    //计算范围内第一个符合的数
    if (m % lcm == 0) {
        pre = m;
    } else {
        pre = m + (lcm - m % lcm);
    }
    //计算范围内最后一个符合的数
    if (n % lcm == 0) {
        suf = n;
    } else {
        suf = n - (n % lcm);
    }

    //计算符合的个数
    long count = (suf - pre) / lcm + 1;
    return count;
}

private static int leastCommonMultiple(int x,int y) {
    int max = (x > y) ? x : y;
    int ans = max;
    for (int i = max; i <= x * y; i++) {
        if ((i % x == 0) && (i % y == 0)) {
            System.out.println("最小公倍数为:" + i);
            ans = i;
            break;
        }
    }
    return ans;
}