"Coding Interview Guide" -- 阶乘
【题目】
给定一个非负整数N,返回N!结果的末尾为0的数量
例如,3! = 6,结果末尾没有0,则返回值为0;5!=120,结果的末尾有1个0,所以返回值为1; 1000000000!,结果的末尾有249999998个0,返回249999998
【分析】
N! = N*(N-1)*(N-2)*...*2*1
可能最直接的方法是直接算出N的阶乘的值,但是阶乘的结果通常非常大,当N=13时,就会发生溢出,所以直接计算阶乘值的方法不合适。
考虑在什么情况下会产生0,1*0 = 0(0! = 1,1! = 1),2*5 = 10,4*5 = 2*(2*5) = 20,...,4*25 = (2*5)*(2*5) = 100 ,...,由此可见,N中具有因子2和5的时候就会产生0,有多少对2和5就会产生多少个0,而N中因子2的个数始终是不少于5的个数,所以,只需要统计N的阶乘中具有多少个因子5,就能知道N的阶乘结果的末尾有多少个0
那么该如何求N的阶乘中有多少个因子5呢,有两个方法:
1、N的阶乘等于1~N这个序列的乘积,1~N中有的数含有因子5,有的数不含。计算1~N中所有含有因子5的数总共含有有多少个因子5,结果即为0的个数
1 public int zeroNum(int num) 2 { 3 if(num <= 0) { return 0; } 4 5 int res = 0; 6 int cur = 0; 7 for(int i = 5; i <= num; i = i + 5) //只需计算含有因子5的数所包含的因子5的具体个数 8 { 9 cur = i; 10 while(cur % 5 == 0) 11 { 12 res++; 13 cur /= 5; 14 } 15 } 16 return res; 17 }
对代码中的每一个数i来说,计算其所含有的因子5的个数的时间复杂度是log(i)(以5为底),i=N时为log(N),一共有N个数,所以时间复杂度为O(NlogN)
2、对于N!,它由1~N的乘积得到,即N!的因子为1,2,3,4,5,6,7,8,9,10...,15...,20...,25...,30...,35...,40...,45...,50...,55...,60...,
65...,70...,75...,80...,85...,90...,95...,100...,105...,110...,115...,120...,125...,130...
观察5,10,15,20,25,30,35...,100,105,110,115,120,125,130等发现每5个含有0个因子5的数组成一组,第5个数就含有1个因子5;再观察25,50,75,100,125等发现每5个含有1个因子5的数组成一组,该组的第5个数就含有2个因子5;再观察125,250,375等发现每5个含有2个因子5的数组成一组,该组的第5个数就含有3个因子5;以此类推,每5个含有i个因子5的数组成一组,该组的第5个数就含有(i+1)个因子5...(注意上面说的是含有,是>=的意思,而不是只含有)
所以,N!的结果中0的个数 = N/5 + N/(5^2) + N/(5^3) + ... + N/(5^i)(i一直增长,直到(5^i) > N) (其中N/5表示每5个数就会有1个因子5;N/(5^2)表示每5^2个数就会有1个因子5,本来按照上面的分析是有2个的,但因为其中的一个因子5已经在N/5中计算过了,所以此处不必重复计算,N/(5^3)表示每5^3个数就会有1个因子5,本来应该有3个的,但是其中的2个已经在N/5和N/(5^2)中计算过...)
1 public int zeroNum(int num) 2 { 3 if(num <= 0) { return 0; } 4 5 int res = 0; 6 while(num != 0) // N/5 + N/(5^2) + N/(5^3) + ... + N/(5^i),直到(5^i) > N 7 { 8 num /= 5; 9 res += num; 10 } 11 return res; 12 }
方法2的时间复杂度为O(logN)(以5为底)
来源:左程云老师《程序员代码面试指南》