LeetCode 172. 阶乘后的零:从暴力到最优,拆解解题核心

刷LeetCode时,遇到「阶乘后的零」这道题,初看简单,实则藏着优化的小技巧。今天就从题目本身出发,一步步拆解两种解题思路,对比暴力解法和最优解法的差异,帮大家吃透这道经典题目,也掌握一类数学问题的解题逻辑。

一、题目解读:读懂「尾随零」的本质

题目很简单:给定一个整数n,返回n!(n的阶乘)结果中尾随零的数量。提示里明确了n! = n × (n-1) × (n-2) × ... × 3 × 2 × 1。

这里有个关键误区:很多人会以为尾随零的数量是由阶乘中10的个数决定的------确实没错,但10是2×5的乘积,而阶乘中2的数量永远比5多(比如每两个数就有一个是2的倍数,每五个数才有一个是5的倍数)。所以,尾随零的数量,本质上是阶乘中质因数5的个数

搞懂这一点,解题就成功了一半。接下来我们看两种解法,从基础到进阶。

二、解法一:暴力枚举,逐个统计5的个数

最直观的思路:遍历从1到n的每一个数,统计每个数中包含的质因数5的个数,最后累加总和,就是尾随零的数量。

对应代码如下(TypeScript):

typescript 复制代码
function trailingZeroes_1(n: number): number {
  let ans = 0;
  for (let i = 1; i <= n; i++) {
    let temp = 0;
    let j = i;
    while (j % 5 === 0) {
      temp++;
      j = Math.floor(j / 5);
    }
    ans += temp;
  }
  return ans;
};

代码拆解

  • 外层for循环:遍历1到n的每一个数i,逐个分析每个数包含的5的个数。

  • 内层while循环:对于当前数i(用j临时存储,避免修改i),判断是否能被5整除;如果能,就计数temp加1,同时将j除以5(继续判断是否有多个5,比如25=5×5,需要统计2个5)。

  • ans累加:将每个数的temp(5的个数)累加到结果中,最终返回ans。

优缺点分析

优点:逻辑简单,容易理解,适合新手入门,能快速想到这种思路。

缺点:效率较低。当n很大时(比如n=10^5),for循环要执行n次,每个数又可能执行多次while循环,时间复杂度是O(n×log₅n),会出现超时情况。

三、解法二:数学优化,直接计算5的总个数

既然暴力解法效率不高,我们就需要优化思路。结合前面的核心结论------尾随零的数量=质因数5的个数,我们可以用数学公式直接计算,无需逐个遍历。

核心逻辑:

  • n/5:计算1到n中,至少包含1个5的数的个数(比如n=25,有5、10、15、20、25,共5个,25/5=5)。

  • n/25:计算1到n中,至少包含2个5的数的个数(比如25,包含2个5,25/25=1;50包含2个5,以此类推)------这些数在n/5中已经被统计过1次,这里需要再额外统计1次。

  • n/125:计算1到n中,至少包含3个5的数的个数(比如125,包含3个5),同样需要额外统计1次。

  • 以此类推,直到n除以5的某次幂结果为0,停止计算,累加所有结果,就是质因数5的总个数。

对应代码如下(TypeScript):

typescript 复制代码
/**
 * n/5 = 有多少个数 至少含 1 个 5
 * n/25 = 有多少个数 额外多 1 个 5(25=5×5)
 * n/125 = 有多少个数 再额外多 1 个 5(125=5×5×5)
 * 以此类推......
 * @param n 
 * @returns 
 */
function trailingZeroes_2(n: number): number {
  let ans = 0;
  while (n !== 0) {
    n = Math.floor(n / 5);
    ans += n;
  }
  return ans;
};

代码拆解

  • while循环:每次将n除以5(向下取整),得到当前层级(51、52、5^3...)中包含5的数的个数。

  • ans累加:将每次除以5的结果累加到ans中,直到n变为0(此时再除以5结果为0,无需继续统计)。

举个例子:n=25

  • 第一次循环:n=25→Math.floor(25/5)=5,ans=5(统计5、10、15、20、25各1个5)。

  • 第二次循环:n=5→Math.floor(5/5)=1,ans=5+1=6(统计25额外的1个5)。

  • 第三次循环:n=1→Math.floor(1/5)=0,循环结束,返回6。

验证:25!的尾随零数量确实是6,和代码计算结果一致。

优缺点分析

优点:效率极高,时间复杂度是O(log₅n),即使n=109,循环也只需要执行不到10次(510=9765625,5^13=1220703125),完全不会超时。

缺点:逻辑需要稍微转个弯,需要理解「多次除以5」的本质,新手可能需要多琢磨一下。

四、两种解法对比&总结

解法 时间复杂度 空间复杂度 核心思路 适用场景
暴力枚举(trailingZeroes_1) O(n×log₅n) O(1) 逐个统计每个数的质因数5个数 n较小,新手理解思路
数学优化(trailingZeroes_2) O(log₅n) O(1) 累加n/5、n/25、n/125...的结果 n较大,追求高效解题

五、解题关键&拓展思考

解题关键

  1. 抓住核心:尾随零的数量由质因数5的个数决定,而非10的个数(因为2的数量足够多)。

  2. 优化思路:避免逐个遍历,利用数学规律,直接计算不同层级(51、52...)中5的个数,大幅提升效率。

拓展思考

这道题的本质是「质因数分解」的应用,类似的题目还有「统计阶乘中某个质因数的个数」,解题思路可以复用:统计n/k + n/k² + n/k³ + ... 直到结果为0。

比如,如果题目问n!中质因数2的个数,思路完全一样,只是把5换成2即可------但注意,此时2的数量会比5多,所以如果是求尾随零,还是要统计5的个数哦。

六、总结

LeetCode 172这道题,看似简单,却能很好地考察我们「从暴力到优化」的解题思维。新手可以先写暴力解法,理解核心逻辑;再琢磨数学优化思路,掌握高效解题的技巧。

相关推荐
军军君012 小时前
数字孪生监控大屏实战模板:可视化数字统计展示
前端·javascript·vue.js·typescript·echarts·数字孪生·前端大屏
轻微的风格艾丝凡2 小时前
三相不平衡电流调试经验记录
算法·dsp
此刻觐神2 小时前
IMX6ULL开发板学习-03(Linux文件相关命令)
前端·chrome
老虎06272 小时前
LeetCode热题100 刷题笔记(第五天)双指针法 「 三数之和 」
笔记·算法·leetcode
沉淀粉条形变量2 小时前
rust 单例模式
开发语言·单例模式·rust
光电笑映2 小时前
C++11 新特性全解:语法糖、容器进化与可调用对象包装
开发语言·c++
汀、人工智能2 小时前
[特殊字符] 第97课:前K个高频元素
数据结构·算法·数据库架构··数据流·前k个高频元素
沉鱼.442 小时前
第十四届题目
数据结构·算法
qq_白羊座2 小时前
Langchain、Cursor、python的关系
开发语言·python·langchain