昨天看到一个编程题,以为很简单,结果还费了一番周折才做出来。
题目:设计一个算法,计算出n阶乘中尾部零的个数
11! = 39916800,因此应该返回 2
思路: 最无脑的办法就是直接求出阶乘的值,然后统计尾部的0:
1 class Solution { 2 public: 3 /* 4 * @param n: A long integer 5 * @return: An integer, denote the number of trailing zeros in n! 6 */ 7 long long trailingZeros(long long n) { 8 if(n==0||n==1) 9 {return 0;} 10 11 long long result=1; 12 // write your code here, try to do it without arithmetic operators. 13 for(long long i=1;i<=n;i++) 14 { 15 result = result*i; 16 } 17 int count=0; 18 while(result%10==0) 19 { 20 count++; 21 result = result/10; 22 } 23 return count; 24 } 25 };
这种方法简单粗暴,但是如果给定n的值很大的话,求阶乘的计算量非常大。
下面就有了第二种思路,在乘法计算里,只要乘以一个10或者10的个位数倍数,结果的后面就会增加一个0。
因为n的阶乘中一定会有n/2个偶数,所以我们只需要考虑有多少个5的倍数,结果的末尾就会增加多少个0。
这里还要考虑到一种情况就是,如果被乘数是5的n次方的倍数话,就结果的末尾增加n个0,例如25可以表示成5*5,一定可以找到一个4的倍数和他相乘使得结果末尾增加两个0;
那么到这里,思路就可以确认为:判断有多少个被乘数是5的倍数,如果是5的倍数的话,再判断这个数是不是5的指数的倍数。就可以确认结果的末尾到底有多少个0
代码如下:
class Solution { public: /* * @param n: A long integer * @return: An integer, denote the number of trailing zeros in n! */ long long trailingZeros(long long n) { if(n==0||n==1) {return 0;} int count=0; long long temp = 0; // write your code here, try to do it without arithmetic operators. for(long long i=1;i<=n;i++) { temp = i; while(temp%5==0) { count++; temp = temp/5; } } return count; } };
这样虽然优化了很多,但是时间复杂度是O(N/5)~=O(N),还是没有达到O(logN)的要求,
最开始想到我要看从1~n里有多少个5的倍数,只需要直接用n除以5就可以知道结果了,但是5的倍数里面还包含了25、125······这些5的多次幂。
如果遇到这些数的话,结果就不是加1而是加2加3····;
但是如果换个思路,我们在统计5的倍数的时候,已经就吧25,125····这些数统计进去了,25的倍数就是比5的倍数能多提供一个0;125的倍数就是能在5的倍数和25的倍数的基础上再多提供一个0····
依此类推,那我们需要求的结果其实就是
n/5 + n/25 + n/125 + n/625······
代码实现:
class Solution { public: /* * @param n: A long integer * @return: An integer, denote the number of trailing zeros in n! */ long long trailingZeros(long long n) { long long count=0; long long a = n/5; while(a>0) { count=count+a; a=a/5; } return count; } };