🎉前言🎉

如果你觉得算法太难,也可以打卡社区最新推出《C语言入门100例》,学习基础的套路,帮助你更扎实的掌握。


目录

一、知识复习

二、最大公因数

三、最小公倍数

四、简单题 

五、困难题  


 一、知识复习

我们之前学习了很多关于质数的算法思想,现在来回顾一下吧。

·素数的判定:遍历 i*i<=n 的所有数(试除法)

·素数的筛选:①双重循环的试除法    ②埃氏筛   ③欧拉筛               素数筛选法

·算数基本定理:任何一个数都可以唯一分解成质数的乘积(理论基础)

求最大公因数Python 求最大公因数的30道题_算法

 ·因子数的计算:根据算数基本定理,利用素数筛选的方法,将一个数分解成多个质因子相乘。由于任何一个因子都是几个质因子相乘得到的,因此我们有:

求最大公因数Python 求最大公因数的30道题_算法_02

 【如n=2*2*2*3*3*5,则因数数有 (3+1)* (2+1)*(1+1)个。对于质因数2来说,可以取得范围在0~3之间,所以一共有3+1种选择,依次类推】

·因子和:                                                                                                                          因子和

求最大公因数Python 求最大公因数的30道题_求最大公因数Python_03

【以n=2*2*2*3*3为例。如果只取一个2,那么3的取法有三种,这一分支的和为2*(3^0+3*1+3*3),而2的取法可以为0~3,根据上面的式子利用乘法分配律可得(2^0+2^1+2^2+2^3)*(3^0+3*1+3*3)对上面的式子不断推演,就可以得出因子和】

二、最大公因数

根据算数基本定理,我们可以将任意两个式子分解成如下的形式:

求最大公因数Python 求最大公因数的30道题_算法_04

 而最大公因数则满足下面的式子:

求最大公因数Python 求最大公因数的30道题_c语言_05

辗转相除法:

int gcd(int a, int b)
{
	return !b ? a : gcd(b , a % b);
}

先说是怎么想到的:

假设两个数的最大公因数为k,则a=m*k,b=n*k,在这里mn互为质数,进行辗转相除的过程中mn最后一定会将mn其中一个消成1,否则a%b不可能为0,因为mn互为质数。那么1*k的数就是我们的最大公因数了。

再说说这段代码:

应用条件运算符,除非b=0(此时a可以整除b,说明mn其中一个消成0了),否则持续递归 

三、最小公倍数

上面提到,最小公因数实际为x,y的较小值。而最大公倍数则是两者的较大值。

求最大公因数Python 求最大公因数的30道题_求最大公因数Python_06

(x1 * x2)/ min (x1, x2)  ,所以我们可以写出以下代码:

int gcd(int a, int b)
{
	return !b ? a : gcd(b , a % b);
}
int lcm(int a, int b)
{
	return a / gcd(a, b) * b;
}

为什么不写成 a * b / gcd(a, b)

a,b都是在整数范围内,而a*b则可能溢出,所以先除再乘避免溢出。看,微小的差别也可以带来意想不到的结果。


 四、简单题 

找出数组的最大公约数https://leetcode-cn.com/problems/find-greatest-common-divisor-of-array/

①题目呈现

求最大公因数Python 求最大公因数的30道题_算法_07

②思路分析

题目很简单,运用上面讲的gcd函数就可以解决 

③参考

int gcd(int a, int b)
{
    return !b ? a : gcd(b , a % b);
}

int findGCD(int* nums, int numsSize)
{
    int max = 1;
    int min = 1000;
    for (int i = 0;i < numsSize; i++)
    {
        if(nums[i] > max)
            max = nums[i];
        if(nums[i] < min)
            min = nums[i];
    }
    return gcd(max, min);
}

 ⑤困难题  

①题目呈现

求最大公因数Python 求最大公因数的30道题_最大公因数_08

求最大公因数Python 求最大公因数的30道题_leetcode_09

 ②思路分析

本题的难点在于子序列的情况有很多,并且我们设计的gcd函数只能求两个数的最大公因数,给我们一串的数字求出最大公因数,从正面突破显然十分困难。但坚持一个原则:正难则反。我们能否逆向思维对所有的情况进行枚举呢?我们只需要找到一种逻辑使得他能够遍历所有的情况即可,因此我们选择最简单最稳定也最容易表示的公因数作为我们的切入点。我们以公因数为标准进行枚举,代码如下:

③参考 

#define maxn 200001//数组可以适当开大一点,这样我们就没必要考虑边界问题
bool f[maxn];//bool类型用来存储nums数组里的数字是否出现过

int gcd(int a, int b)//求最大公因数函数
{
    return !b? a : gcd(b,a % b);
}

int MAX(int a, int b)//求两数较大值
{
    return a > b? a : b;
}

int countDifferentSubsequenceGCDs(int* nums, int numsSize)
{
    int m = 0;
    int cnt = 0;
    memset(f, 0, sizeof(f));//初始化为0,认为所有数字没有出现

    for(int i = 0;i < numsSize; i++)//nums数组中出现的数则在f[]中记录一下
    {
        f[nums[i]] = 1;//这里不是f[nusm[i]+1],因为下面使用的时候也没有+1
        m = MAX(m, nums[i]);
    }

    for(int i = 1; i <=m; i++)//枚举所有可能为公约数i
    {
        int tmp =0;

        for(int j = i; j <= m ; j+=i)//遍历数组f[],从中找出nums数组中出现过的数
        {
            if(f[j])
                tmp = gcd(tmp,j);
        }

        if(tmp == i)//若i与nums数组中的数比对后仍然是最大公因数说明确实是一个解
            cnt++;
    }

    return  cnt;
}

题后反思: 

 这道题目也给了我们启发:一个复杂的问题只要找到一个合适的逻辑能够遍历所有情况就可以解决问题。我们在寻找枚举条件时抓住本质有意向不到的效果。