尾部的零
设计一个算法,计算出n阶乘中尾部零的个数
样例
样例 1:
输入: 11
输出: 2
样例解释:
11! = 39916800, 结尾的0有2个。
方案一:是想把阶乘按照阶乘的表达式进行循环,对每一个因数%5进行判断,若有可以被整除的继续%5,这样虽然达到了要求,但是由于太过暴力,导致所给阶乘数较大时超时。代码如下
public static long trailingZeros(long n) {
if(n==0){
return 1;
}else{
int count = 0;
int temp = 0;
for(int j=(int) n;j>0;j--){
temp = j;
for(int k=5;k<=j;k+=0){
if(temp%k==0){
count++;
temp /= 5;
}else{
break;
}
}
}
return count;
}
}
方案二、
首先对问题进行转化,末尾的一个0对应一个10,一个10又能分解为2*5,易知所有因数中2的个数远远多于5,故0的个数即为有多少个因数5的个数。
1000!的末尾0的个数
1000/5 + 1000/25 + 1000/125 + 1000/625
= 200 + 40 + 8 + 1
= 249(个)
26! 的末尾0的个数
26/5 + 26/25 = 6(个)5
从以上两个例子可以发现规律如下:
遍历所有小于等于n(所给阶乘的基数)的5及5的倍数的因数。
再整除所给n,求和便可得到所有5的个数。
代码如下:
public static long trailingZeros(long n) {
int count = 0;
for(int i=5;i<=n;i*=5){
count += n/i;
}
return count;
}
果然,发现规律之后达到了很好的简化效果。
方案三、然而在已经提高了运行速度的基础上,当给出更大的数时,还是发生了超出时间限制。看来要再次加大循环判断的进度。这次直接去网上搜到了一下方案三的算法。
public long trailingZeros(long n) {
long count = 0;
for(int i = 1; Math.pow(5,i) <= n; i++) {
count += n / (long)Math.pow(5,i);//注意强制类型转换
}
return count;
}
原文:https://blog.youkuaiyun.com/wutingyehe/article/details/46882181
细致观察之后发现方案二和方案三的循环次数都一样,但是方案三比方案二运行时间要快。有可能是因为幂次运算要比乘法运算速度快的原因吧,要注意。
总结:对此题,首先应该分析并转化问题,然后再由几个特例提取普遍规律。
再进行算法设计。