【LeetCode 笔记】数学


69. x 的平方根

题目描述
解题思路:

  1. 暴力试根法
    从0开始遍历,找到 i 2 > x i^2 > x i2>x即可
    时间复杂度: O ( n ) O(n) O(n)
    空间复杂度: O ( n ) O(n) O(n)
    缺点:i 越大,越容易溢出
int mySqrt(int x){
    if(!x)
        return x;
    long long i = 0;
    while(i * i < x)
        i++;
    if(i * i == x)
        return i;
    return i - 1;
}
  1. 牛顿法

以上图片来自《牛顿法与拟牛顿法》
详细证明也参考此文章

不讨论其数学证明,直接使用牛顿法结论:
公式
结论

int mySqrt(int x){
    if(x < 2)
        return x;
    double x0, x1;
    x0 = x;
    x1 = (x0 + x / x0) / 2.0;
    while(fabs(x0 - x1) >= 1.0){
        x0 = x1;
        x1 = (x0 + x / x0) / 2.0;
    }
    return (int)x1;
}

时间复杂度: O ( log ⁡ N ) \mathcal{O}(\log N) O(logN),此方法是二次收敛的。

空间复杂度: O ( 1 ) \mathcal{O}(1) O(1)

注:C语言中求绝对值的函数有两种,分别为abs()、fabs()。abs()函数用来对整型变量求绝对值,fabs()函数用来对浮点型变量求绝对值。abs()属于头文件“stdlib.h”,fabs()属于头文件“math.h”。


172. 阶乘后的零

给定一个整数 n,返回 n! 结果尾数中零的数量。
输入: 3
输出: 0
解释: 3! = 6, 尾数中没有零。

解题思路
参考大佬的思路:

首先题目的意思是末尾有几个0
比如6! = 【1 × 2 × 3 × 4 × 5 × 6】
其中只有2×5末尾才有0,所以就可以抛去其他数据 专门看2、5 以及其倍数,毕竟 4 × 25末尾也是0
比如10! = 【2 × 4 × 5 × 6 × 8 × 10】
其中 4能拆成2×2 、10能拆成2×5
所以10! = 【2 ×(2 ×2)× 5 ×(2 × 3)×(2 × 2 × 2) ×(2 × 5)】
一个2和一个5配对 就产生一个0,所以10!末尾2个0


转头一想 2肯定比5多 所以只数5的个数就行了

对于一个数的阶乘,就如之前分析的,5 的因子一定是每隔 5 个数出现一次,也就是下边的样子。
n ! = 1 ∗ 2 ∗ 3 ∗ 4 ∗ ( 1 ∗ 5 ) ∗ . . . ∗ ( 2 ∗ 5 ) ∗ . . . ∗ ( 3 ∗ 5 ) ∗ . . . ∗ n n! = 1 * 2 * 3 * 4 * (1 * 5) * ... * (2 * 5) * ... * (3 * 5) *... * n n!=1234(15)...(25)...(35)...n
因为每隔 5 个数出现一个 5,所以计算出现了多少个 5,我们只需要用 n/5 就可以算出来。

. . . ∗ ( 1 ∗ 5 ) ∗ . . . ∗ ( 1 ∗ 5 ∗ 5 ) ∗ . . . ∗ ( 2 ∗ 5 ∗ 5 ) ∗ . . . ∗ ( 3 ∗ 5 ∗ 5 ) ∗ . . . ∗ n ... * (1 * 5) * ... * (1 * 5 * 5) * ... * (2 * 5 * 5) * ... * (3 * 5 * 5) * ... * n ...(15)...(155)...(255)...(355)...n
每隔 25 个数字,出现的是两个 5,所以除了每隔 5 个数算作一个 5,每隔 25 个数,还需要多算一个 5。
也就是我们需要再加上 n / 25 个 5。
同理我们还会发现每隔 5 ∗ 5 ∗ 5 = 125 5 * 5 * 5 = 125 555=125 个数字,会出现 3 个 5,所以我们还需要再加上 n / 125 。


综上,规律就是每隔 5 个数,出现一个 5,每隔 25 个数,出现 2 个 5,每隔 125 个数,出现 3 个 5… 以此类推
最终 5 的个数就是 n / 5 + n / 25 + n / 125... n / 5 + n / 25 + n / 125 ... n/5+n/25+n/125...
写程序的话,如果直接按照上边的式子计算,分母可能会造成溢出。所以算 n / 25 的时候,我们先把 n 更新,n = n / 5,然后再计算 n / 5 即可。后边的同理。

int trailingZeroes(int n){
    int count = 0;
    while(n > 0){
        count += n / 5;	//统计当前n的 因数5的个数
        n /= 5;			//更新n,进行下一次循环统计
    }
    return count;
}

或递归形式

int trailingZeroes(int n){
    return n == 0 ? 0 : (n / 5 + trailingZeroes(n / 5));
}

204. 计数质数

统计所有小于非负整数 n 的质数的数量。

思路: Eratosthenes 筛选法

Sieve_of_Eratosthenes_animation.gif

图片来自 Wikipedia - Sieve_of_Eratosthenes_animation.gif

int countPrimes(int n){
    if(n == 0 || n == 1)    //0和1都不是质数
        return 0;

    int  *flag = malloc(sizeof(int) * n);
    memset(flag, 0 ,sizeof(int) * n);
    long int i, j, count = 0;	//int溢出,用long int

    for(i = 2; i < n; i++){
        if(flag[i]) //合数,flag[i] != 0
            continue;
        count++;    //遇到质数进行统计
        flag[i] = 1;
        for(j = i * i; j < n; j += i){  //i的倍数全为合数
            flag[j] = 1;
        }
    }
    free(flag);
    return count;
}

最大公因数 & 最小公倍数

求最小公倍数算法:
最小公倍数 = 两整数的乘积 ÷ 最大公约数

求最大公约数算法: 辗转相除法
有两整数a和b:
① a%b得余数c
② 若c=0,则b即为两数的最大公约数
③ 若c≠0,则a=b,b=c,再回去执行①
例如求27和15的最大公约数过程为:
27÷15 余1215÷12余312÷3余0因此,3即为最大公约数

//求a,b的最大公因数
int GCD(int a, int b) {
    int temp;
    do {
        temp = a % b;
        if(temp == 0)
            return b;
        else {
            a = b;
            b = temp;
        }
    } while(temp != 0);
}

//求m和n的最小公倍数
int LCM(int m, int n) {
    int x, y, c;
    x = m; y = n;
    while(m != 0) {
        c = n % m;
        n = m;
        m = c;
    }
    return (x * y / n);
}

367. 有效的完全平方数

给定一个正整数 num,编写一个函数,如果 num 是一个完全平方数,则返回 True,否则返回 False。
说明:不要使用任何内置的库函数,如 sqrt
输入:16
输出:True

解题思路

完全平方数可以通过累加从1往后的奇数找到,
1 = 1;
4 = 1 + 3;
9 = 1 + 3 + 5;
16 = 1 + 3 + 5 + 7;

bool isPerfectSquare(int num){
    if(num == 0)
        return false;
    int i = 1;
    
    while(num > 0){
        num -= i;	//通过一步步减去递增奇数后是否为零来判断
        i += 2;
    }
    return num == 0 ? true : false;
}

LCP 06. 拿硬币

桌上有 n 堆力扣币,每堆的数量保存在数组 coins 中。我们每次可以选择任意一堆,拿走其中的一枚或者两枚,求拿完所有力扣币的最少次数。

示例 1
输入: [4,2,1]
输出: 4
解释: 第一堆力扣币最少需要拿 2 次,第二堆最少需要拿 1 次,第三堆最少需要拿 1 次,总共 4 次即可拿完。

示例 2
输入: [2,3,10]
输出: 8

限制:
1 <= n <= 4
1 <= coins[i] <= 10

int minCount(int* coins, int coinsSize){
    int res = 0;
    
    for(int i = 0; i < coinsSize; i++){
        //一堆硬币最多拿两个,因此一堆2个2个拿最多拿 coins[i] / 2 次
        //拿完两个还剩 coins[i] % 2 个,即还需拿1次或0次
        
        res += coins[i] / 2 + coins[i] % 2;
    }
    
    return res;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Beta Lemon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值