题解 | #阶乘末尾非零数字#

阶乘末尾非零数字

https://www.nowcoder.com/practice/248c8fbee56e491aa147b67b9c082da0

题目链接

HIGH3 阶乘末尾非零数字

题目描述

给定一个正整数 n,请求出 n! (n的阶乘) 的十进制表示中,从右往左数第一个非零的数字。

解题思路

这个问题的核心挑战在于 n! 的值会非常快地增长,对于稍大的 n(例如 n=20),其阶乘结果就会超出标准64位整型(long long)的存储范围。因此,直接计算出 n! 的完整值再提取末位数字是不可行的。

我们需要一种在计算过程中就能控制数值大小的方法。思路如下:

  1. 迭代计算:我们可以模拟阶乘的计算过程,从 1 乘到 n。维护一个变量 res,用于存储我们关心的“末尾部分”的乘积。

  2. 移除尾随零:阶乘结果中的尾随零是由因子 2 和 5 相乘(2 * 5 = 10)产生的。我们只关心第一个非零数字,所以每当乘积 res 的末尾出现 0,就应该立即移除它。这可以通过一个循环实现:只要 res % 10 == 0,就执行 res /= 10

  3. 防止溢出:即使移除了尾随的零,res 的值在乘以 i 时仍然可能增长得非常快,最终导致溢出。为了解决这个问题,我们可以在每次移除尾零后,对 res 进行取模操作,只保留其末尾的几位数字。例如 res %= 1000000000

    • 为什么这是可行的?因为我们最终只关心结果的个位数字 (res % 10)。保留末尾的几位(如9位)作为“缓冲区”,足以确保在下一次乘以 ii 不会太大)并移除尾零后,得到的个位数字是正确的。这个缓冲区保证了我们不会因为过早地取模而丢失影响最终结果个位数的关键信息。
  4. 特殊情况:根据数学定义,0! = 1,所以当输入为 0 时,应输出 1。

综上所述,我们的算法是:

  • 初始化 res = 1
  • i = 1 循环到 n
  • 在循环中,res *= i
  • 移除 res 的所有尾随零。
  • res 取一个较大的模(如 100000)以防止溢出。
  • 循环结束后,res % 10 就是最终答案。

代码

#include <iostream>

using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    long long n;
    cin >> n;

    if (n == 0) {
        cout << 1 << endl;
        return 0;
    }

    long long res = 1;
    for (long long i = 1; i <= n; ++i) {
        res *= i;
        while (res % 10 == 0) {
            res /= 10;
        }
        res %= 1000000000; // 保留足够的末位以防止溢出和保证精度
    }

    cout << res % 10 << endl;

    return 0;
}
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long n = sc.nextLong();

        if (n == 0) {
            System.out.println(1);
            return;
        }

        long res = 1;
        for (long i = 1; i <= n; i++) {
            res *= i;
            while (res % 10 == 0) {
                res /= 10;
            }
            res %= 1000000000; // 保留缓冲区
        }

        System.out.println(res % 10);
    }
}
n = int(input())

if n == 0:
    print(1)
else:
    res = 1
    for i in range(1, n + 1):
        res *= i
        while res % 10 == 0:
            res //= 10
        # Python可以处理大整数,但我们仍然可以取模
        # 以保持数字较小,从而可能加快计算速度。
        res %= 1000000000
    
    print(res % 10)

算法及复杂度

  • 算法:模拟、数学
  • 时间复杂度:。我们有一个主循环,从 1 迭代到 n。在循环内部,while 循环用于移除尾随零,其执行次数取决于 resi 中包含的因子 5 和 2 的数量,这个次数相对较小。因此,时间复杂度近似为
  • 空间复杂度:。我们只需要常数级别的额外空间。
全部评论

相关推荐

不愿透露姓名的神秘牛友
07-09 13:05
TMD找工作本来就烦,这东西什么素质啊😡
Beeee0927:hr是超雄了,不过也是有道理的
点赞 评论 收藏
分享
北漂的牛马人:211佬,包进的,可能是系统问题
点赞 评论 收藏
分享
06-13 17:33
门头沟学院 Java
顺序不记了,大致顺序是这样的,有的相同知识点写分开了1.基本数据类型2.基本数据类型和包装类型的区别3.==和equals区别4.ArrayList与LinkedList区别5.hashmap底层原理,put操作时会发生什么6.说出几种树型数据结构7.B树和B+树区别8.jvm加载类机制9.线程池核心参数10.创建线程池的几种方式11.callable与runnable区别12.线程池怎么回收线程13.redis三剑客14.布隆过滤器原理,不要背八股,说说真正使用时遇到了问题没有(我说没有,不知道该怎么回答了)15.堆的内存结构16.自己在写项目时有没有遇见过oom,如何处理,不要背八股,根据真实经验,我说不会17.redis死锁怎么办,watchdog机制如何发现是否锁过期18.如何避免redis红锁19.一个表性别与年龄如何加索引20.自己的项目的QPS怎么测的,有没有真正遇到大数量表21.说一说泛型22.springboot自动装配原理23.springmvc与springboot区别24.aop使用过嘛?动态代理与静态代理区别25.spring循环依赖怎么解决26.你说用过es,es如何分片,怎么存的数据,1000万条数据怎么写入库中27.你说用limit,那么在数据量大之后,如何优化28.rabbitmq如何批次发送,批量读取,答了延迟队列和线程池,都不对29.计网知不知道smtp协议,不知道写了对不对,完全听懵了30.springcloud知道嘛?只是了解反问1.做什么的?短信服务,信息量能到千万级2.对我的建议,基础不错,但是不要只背八股,多去实际开发中理解。面试官人不错,虽然没露脸,但是中间会引导我回答问题,不会的也只是说对我要求没那么高。面完问我在济宁生活有没有困难,最快什么时候到,让人事给我聊薪资了。下午人事打电话,问我27届的会不会跑路,还在想办法如何使我不跑路,不想扣我薪资等。之后我再联系吧,还挺想去的😭,我真不跑路哥😢附一张河科大幽默大专图,科大就是大专罢了
查看30道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务