给定一个整数 n
,返回 n!
结果中尾随零的数量。
提示 n! = n * (n - 1) * (n - 2) * ... * 3 * 2 * 1
示例 1:
输入:n = 3 输出:0 解释:3! = 6 ,不含尾随 0
示例 2:
输入:n = 5 输出:1 解释:5! = 120 ,有一个尾随 0
示例 3:
输入:n = 0 输出:0
解法一
之前小红书面试的时候碰到的一道题,没想到又是 leetcode 的原题。这种没有通用解法的题,完全依靠于对题目的分析理解了,自己当时也是在面试官的提示下慢慢出来的,要是想不到题目的点,还是比较难做的。
首先肯定不能依赖于把阶乘算出来再去判断有多少个零了,因为阶乘很容易就溢出了,所以先一步一步理一下思路吧。
首先末尾有多少个 0
,只需要给当前数乘以一个 10
就可以加一个 0
。
再具体对于 5!
,也就是 5 * 4 * 3 * 2 * 1 = 120
,我们发现结果会有一个 0
,原因就是 2
和 5
相乘构成了一个 10
。而对于 10
的话,其实也只有 2 * 5
可以构成,所以我们只需要找有多少对 2/5
。
我们把每个乘数再稍微分解下,看一个例子。
11! = 11 * 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 = 11 * (2 * 5) * 9 * (4 * 2) * 7 * (3 * 2) * (1 * 5) * (2 * 2) * 3 * (1 * 2) * 1
对于含有 2
的因子的话是 1 * 2, 2 * 2, 3 * 2, 4 * 2 ...
对于含有 5
的因子的话是 1 * 5, 2 * 5...
含有 2
的因子每两个出现一次,含有 5
的因子每 5
个出现一次,所有 2
出现的个数远远多于 5
,换言之找到一个 5
,一定能找到一个 2
与之配对。所以我们只需要找有多少个 5
。
直接的,我们只需要判断每个累乘的数有多少个 5
的因子即可。
public int trailingZeroes(int n) {
int count = 0;
for (int i = 1; i <= n; i++) {
int N = i;
while (N > 0) {
if (N % 5 == 0) {
count++;
N /= 5;
} else {
break;
}
}
}
return count;
}
但发生了超时,我们继续分析。
对于一个数的阶乘,就如之前分析的,5
的因子一定是每隔 5
个数出现一次,也就是下边的样子。
n! = 1 * 2 * 3 * 4 * (1 * 5) * ... * (2 * 5) * ... * (3 * 5) *... * n
因为每隔 5
个数出现一个 5
,所以计算出现了多少个 5
,我们只需要用 n/5
就可以算出来。
但还没有结束,继续分析。
... * (1 * 5) * ... * (1 * 5 * 5) * ... * (2 * 5 * 5) * ... * (3 * 5 * 5) * ... * n
每隔 25
个数字,出现的是两个 5
,所以除了每隔 5
个数算作一个 5
,每隔 25
个数,还需要多算一个 5
。
也就是我们需要再加上 n / 25
个 5
。
同理我们还会发现每隔 5 * 5 * 5 = 125
个数字,会出现 3
个 5
,所以我们还需要再加上 n / 125
。
综上,规律就是每隔 5
个数,出现一个 5
,每隔 25
个数,出现 2
个 5
,每隔 125
个数,出现 3
个 5
... 以此类推。
最终 5
的个数就是 n / 5 + n / 25 + n / 125 ...
写程序的话,如果直接按照上边的式子计算,分母可能会造成溢出。所以算 n / 25
的时候,我们先把 n
更新,n = n / 5
,然后再计算 n / 5
即可。后边的同理。
public int trailingZeroes(int n) {
int count = 0;
while (n > 0) {
count += n / 5;
n = n / 5;
}
return count;
}