[LeetCode][172][Factorial Trailing Zeroes]

题目链接:https://leetcode.com/problems/factorial-trailing-zeroes/

题目描述:

Given an integer n, return the number of trailing zeroes in n!.

Note: Your solution should be in logarithmic time complexity.

大概意思就是求N!的末尾0的个数.

这道题一拿过来基本没什么思路,先不想代码,纯粹当做一个数学问题上网搜了一下,很容易得到以下思路:

看从1到n能分解出多少个因子5,因为一个5与一个偶数相乘会有一个0,而偶数的个数是远大于因子5的个数的,所以转换为求因子5的个数。

然后依次看1到n中能被5整除的个数m1,

能被5的平方整除的个数m2,

能被5的立方整除的个数m3,。。。

一直到5的(k+1)次方大于n为止,n!末尾0的个数就等于m1+m2+m3+...+mk.

对于求5的因子的个数,很容易直观的想到第一种解法,就是从1遍历到n的每个数都除以5,可以被5整除的,继续除以5,直到不能整除为止,每整除一次,计数器就加1.代码如下:

先贴第一下自己第一次写下的错误代码:(此段代码别人可忽略)

int trailingZeroes(int n) {
    void division(int m, int c);
    int i,count;
    count = 0;
    for(i = 1; i <= n; i++){
        if(i%5 == 0){
            count++;
            division(i/5, count);
        }
    }
    
    return count;
}

void division(int m, int c){
    if(m%5 == 0){
        c++;
        division(m/5, c);
    }
}

先不说递归函数里的参数传递的错误,这就是个最直观的思考方式(这里自己想到每个可被5整除的的数继续除以5的时候可以用递归,由于代码写得少,开始没有想到用步长和while控制),下面是把递归函数完成的功能用while循环代替,可是再改成while循环的时候又犯了一个错误,如下:
int trailingZeroes(int n) {
    
    int i,count;
    count = 0;
    for(i = 1; i <= n; i++){
        while(i%5 == 0){
            count++;
            i /= 5;
        }
    }
    return count;
}
这里的运行时间又超时了,不是由于这个算法本身的复杂度问题(下面再说),而是因为在while循环里修改了索引变量i的值,正确版本为:
int trailingZeroes(int n) {
    int i,temp;
    int count = 0;
    for(i = 1; i <= n; i++)
    {
	    temp = i;
    	while(temp % 5 == 0)
	    {
		    count++;
		    temp /= 5;
	    }
    }
    return temp;

}

现在分析这个代码的复杂度,因为要从1一直遍历到n共n次,每遇到5的倍数还要加上除以5的次数,即本题所得结果,大概是n/4次,所以计算次数大概是1.25n次,在LeetCode的测试数达到近20亿次的时候,显然运行时超时。

如何减少时间复杂度呢?考虑上文分析的5的次数计算方法,其提到说结果为m1+m2+m3+...+mk,其实只要看n里面有几个5,几个25,。。。几个5的k次方即可,所以直接用n依次除以5的各阶次方,把每次得到的加起来即为所求结果。

对于优化版,第一次用的是乘法,代码如下:

int trailingZeroes(int n) {
    int i;
    int count = 0;
    for(i = 5; i < n; i *= 5){
        count += n/i;
    }
    return count;
}

这样,时间复杂度为 log 5 n,时间复杂度就大大减小了,可是运行还是超时了,为什么呢,因为这里要计算到5的k次方,可以是一个很大的数,这样的乘法也是很危险的,对于测试参数达到了20亿,系统就会爆掉。

所以最后通过的版本是采用除法,代码如下:

int trailingZeroes(int n) {
    int count = 0;
    while(n){
        count += n/5;
        n = n/5;    //换成了除法
    }
    return count;
}

此处,172题终于通过~~~

小结:

1.做题的时候可以先用直观的容易想到的方法解题,然后看代码,如果可以容易发觉的错误在运行前就应该找到,然后再根据错误改代码,并优化代码。

2.有些递归操作,是可以用while或者for循环代替的,要先想着用循环语句解决问题。

3.在做一些循环的时候,乘法可能会超出运行时间,可以改成除法。

4.就是对这一类数学题的解决办法了,对于求N!末尾0的个数,可以转换为求因子5的个数。

令1到n中能被5整除的个数m1,能被5的平方整除的个数m2,能被5的立方整除的个数m3,。。。一直到5的(k+1)次方大于n为止,n!末尾0的个数就等于m1+m2+m3+...+mk.









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值