172. 阶乘后的零——Leetcode每日一题

Posted 期望上岸的鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了172. 阶乘后的零——Leetcode每日一题相关的知识,希望对你有一定的参考价值。

172. 阶乘后的零

给定一个整数 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

提示:

  • 0 < = n < = 1 0 4 0 <= n <= 10^4 0<=n<=104

进阶:你可以设计并实现对数时间复杂度的算法来解决此问题吗?

思路:

  • 尾部的 0 由 2 * 5 得来,2 的数量明显多于 5 的数量,因此只要统计有多少个 5 即可。

优化进阶:

  • 对于一个数 N,它所包含 5 的个数为: N / 5 + N / 5 2 + N / 5 3 + . . . N/5 + N/5^2 + N/5^3 + ... N/5+N/52+N/53+...,其中 N/5 表示不大于 N 的数中 5 的倍数贡献一个 5, N / 5 2 N/5^2 N/52 表示不大于 N 的数中 5 2 5^2 52 的倍数再贡献一个 5 …。

例如:当N为25时, 包含5个数字为:

5、10、15、20、25

总个数为 25 / 5 + 25 / 5 2 = 6 25/5 + 25/5^2 = 6 25/5+25/52=6

类比:

  • 如果统计的是 N! 的二进制表示中最低位 1 的位置,只要统计有多少个 2 即可;
  • 2 的个数为: N / 2 + N / 2 2 + N / 2 3 + . . . N/2 + N/2^2 + N/2^3 + ... N/2+N/22+N/23+...

代码:(Java)

public class TrailingZeroes 

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		int n = 5;
		System.out.println(trailingZeroes(n));
	
	
	public static int trailingZeroes(int n) 
		
		int num = 0;
		for(int i = n; i > 0; i--) 
			int j = i;
			while(j % 5 == 0) 
				j /= 5;
				num++;
			
		
		
		return num;
    

优化进阶:

public class TrailingZeroes 

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		int n = 5;
		System.out.println(trailingZeroes(n));
	
	
	public static int trailingZeroes(int n) 
		
		return n == 0 ? 0 : n / 5 + trailingZeroes(n / 5);
    

运行结果:

复杂度分析:

  • 时间复杂度 O ( l o g n ) O(logn) O(logn)优化进阶后的,优化前的为 O ( n ) O(n) O(n).
  • 空间复杂度:忽略递归带来的额外空间开销,复杂度为 O ( 1 ) O(1) O(1).

题目来源:力扣。

注:仅供学习参考, 如有不足,欢迎指正!

LeetCode.172- 阶乘后的零

题目描述

给定一个整数 n,返回 n! 结果尾数中零的数量。

1.计算阶乘

这种方法速度太慢了,但却是一个好的起点。虽然不会在面试中实现它,但是你可以简单的描述它是个解决问题的办法之一。
解决这个问题的最简单的办法就是计算 n!,然后计算它的末尾数 0 个数。阶乘是通过将所有在 1 和 n 之间的数字相乘计算的。例如:10!=10⋅9⋅8⋅7⋅6⋅5⋅4⋅3⋅2⋅1=3,628,800。因此,可以使用以下算法迭代计算阶乘。

代码展示

Python

def trailingZeroes(self, n: int) -> int:  
    # Calculate n!
    n_factorial = 1
    for i in range(2, n + 1):
        n_factorial *= i
    
    # Count how many 0's are on the end.
    zero_count = 0
    while n_factorial % 10 == 0:
        zero_count += 1
        n_factorial //= 10
        
    return zero_count

在 Java 中,我们需要使用 BigInteger,防止在计算阶乘的过程中溢出。

import java.math.BigInteger;

public int trailingZeroes(int n) {
    
    // Calculate n!
    BigInteger nFactorial = BigInteger.ONE;
    for (int i = 2; i <= n; i++) {
        nFactorial = nFactorial.multiply(BigInteger.valueOf(i));
    }
                    
    // Count how many 0's are on the end.
    int zeroCount = 0;
    
    while (nFactorial.mod(BigInteger.TEN).equals(BigInteger.ZERO)) {
        nFactorial = nFactorial.divide(BigInteger.TEN);
        zeroCount++;
    }
    
    return zeroCount;
}

2.计算因子 5

这种方法也太慢了,但是在解决问题的过程中,它很可能是提出对数方法的一个步骤。
与方法 1 中那样计算阶乘不同,我们可以认识到阶乘末尾的每个 0 表示乘以 10。
那么,我们在计算 n! 时乘以 10 的次数是多少?两个数字 aa 和 bb 相乘。例如,要执行 42⋅75=3150,可以重写如下:542⋅75=2⋅3⋅7⋅3⋅5⋅5
现在,为了确定最后有多少个零,我们应该看有多少对 22 和 55 的因子。在上面的例子中,我们有一对 22 和 55 的因子。

例如,如果 n=16,我们需要查看 1到 16 之间所有数字的因子。我们只对 2 和 5 有兴趣。包含 5因子的数字是 5,10,15,包含因子 22 的数字是 2、4、6、8、10、12、14、16。因为只三个完整的对,因此 16! 后有三个零。

class Solution:
   def trailingZeroes(self, n: int) -> int:
        five = 0
        two = 2
        for i in range(5, n+1):
            cur = i
            while cur % 5 == 0:
                five += 1
                cur //= 5 
                
        for i in range(2, n+1):
            cur = i
            while cur % 2 == 0:
                two += 1
                cur //= 2

        return min(two, five)

这样我们就得到了正确答案,但是我们仍然可以做一些改进。

首先,我们可以注意到因子 2 数总是比因子 5 大。为什么?因为每四个数字算作额外的因子 2,但是只有每 25 个数字算作额外的因子 5。下图我们可以清晰的看见:

在这里插入图片描述

优化1

class Solution:
   def trailingZeroes(self, n: int) -> int:
        five = 0
 
        for i in range(5, n+1):
            cur = i
            while cur % 5 == 0:
                five += 1
                cur //= 5 

        return five

优化2
我们可以做最后一个优化。在上面的算法中,我们分析了从 1 到 n 的每个数字。但是只有 5, 10, 15, 20, 25, 30, … 等等 至少有一个因子 5。所以,偶们不必一步一步的往上迭代,可以五步的往上迭代:因此可以修改为:

def trailingZeroes(self, n: int) -> int:
        
    zero_count = 0
    for i in range(5, n + 1, 5):
        current = i
        while current % 5 == 0:
            zero_count += 1
            current //= 5

    return zero_count

优化3
在方法 2 中,我们找到了一种计算阶乘后的零的个数的方法,而不需要实际计算阶乘。这是通过在 55 的每个倍数上循环,从 5 到 n,并计算 5 的每个倍数中有多少个因子。我们把所有这些数字加起来得到最终结果。

然而,无论是实际上还是对问题的要求来说,方法 2 仍然太慢。为了得出一个足够快的算法,我们需要做进一步改进,这个改进能使我们在对数时间内计算出我们的答案。

def trailingZeroes(self, n: int) -> int:
    zero_count = 0
    while n > 0:
        n //= 5
        zero_count += n
    return zero_count

摘自官方题解 Link

以上是关于172. 阶乘后的零——Leetcode每日一题的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 172. 阶乘后的零(Factorial Trailing Zeroes)

LeetCode.172- 阶乘后的零

LeetCode172. 阶乘后的零

LeetCode 172. 阶乘后的零

leetcode——172.阶乘后的零

LeetCode 172 Factorial Trailing Zeroes(阶乘后的零)(*)