每日一道 LeetCode (41):阶乘后的零

每天 3 分钟,走上算法的逆袭之路。
前文合集
代码仓库
GitHub: https://github.com/meteor1993/LeetCode
Gitee: https://gitee.com/inwsy/LeetCode
题目:阶乘后的零
给定一个整数 n,返回 n! 结果尾数中零的数量。
示例 1:
输入: 3
输出: 0
解释: 3! = 6, 尾数中没有零。
示例 2:
输入: 5
输出: 1
解释: 5! = 120, 尾数中有 1 个零.
说明: 你算法的时间复杂度应为 O(log n) 。
解题方案一:暴力计算
看到这道题,常人的思维最少应该有先把 n! 算出来,再通过 /10 来计算末尾有多少个 0 。
// 暴力硬算
public int trailingZeroes(int n) {
// 先定义第一个数字
BigInteger bi = BigInteger.ONE;
for (int i = 2; i <= n; i++) {
bi = bi.multiply(BigInteger.valueOf(i));
}
// 定义 0 出现的次数
int count = 0;
while (bi.mod(BigInteger.TEN).equals(BigInteger.ZERO)) {
bi = bi.divide(BigInteger.TEN);
count++;
}
return count;
}

我累个擦,直接超时了,然后看下测试用例,竟然执行到一个 4327 的数字,拿 4 位数出来算阶乘,就是计算机来算我都觉得累得慌,这个测试用例我服了,甘拜下风。
么得办法了,看答案吧,我这个智商也就只能想到这种方案了。
解题方案二:计算因子 5
先想一下,啥情况下能产生 0 ,最小的产生因子是 2 * 5 。
借用一个答案上的示例,42 * 75 = 3150 ,这个算式可以拆解如下:
42 = 2 * 3 * 7
75 = 3 * 5 * 5
42 * 75 = 2 * 3 * 7 * 3 * 5 * 5
这里面只出现了一对 2 和 5 ,所以结果只有一个 0 。
接着看另一个案例,比如求 19! ,这里面包含 5 的因子有 5 、10 、15 ,包含 2 的因子有2、4、6、8、10、12、14、16、18 。
可以看到的规律是每 5 个数字就会有一个包含 5 的因子,而在这 5 个数中间,肯定至少有一个包含 2 的因子,实际上是有 2 个,所以就不需要考虑 2 这个因子了,单纯的考虑 5 就好了,那么算法就演变成了我们需要寻找包含 5 的因子,代码如下:
// 计算因子 5
public int trailingZeroes_1(int n) {
int count = 0;
for (int i = 5; i <= n; i += 5) {
int current = i;
while (current % 5 == 0) {
count++;
current /= 5;
}
}
return count;
}

结果又超时了,我心态崩了啊,答案给的都是超时,看这个输入的数字, TM 18 亿,还敢输入数字再大点嘛。
接着刚才的答案往下看。
答案上还给出了另一种方案,不需要每次检查是否可以被 5 整除,还可以直接检查是否可以被 5 的次幂整除:
public int trailingZeroes_2(int n) {
int count = 0;
for (int i = 5; i <= n; i += 5) {
int powerOf5 = 5;
while (i % powerOf5 == 0) {
count += 1;
powerOf5 *= 5;
}
}
return count;
}
这个方案实际上和上面的方案是一样的,没什么本质的区别。
解题方案三:高效的计算因子 5
接着往下看,更加高效的计算方案。
看下面这个阶乘方案:
n! = 1 * 2 * 3 * 4 * (1 * 5) * ... * (2 * 5) * ... * (3 * 5) *... * n
我们的目标是计算出现了多少个 5 ,从上面这个公式看下来,每隔 5 个数就会出现一次,那么我们使用 n / 5 就可以计算出来。
但是还没有结束,接着看下面的公式变化:
... * (1 * 5) * ... * (1 * 5 * 5) * ... * (2 * 5 * 5) * ... * (3 * 5 * 5) * ... * n
从这个公式可以看出来,每隔 5 个数,出现一个 5 ,每隔 25 个数,出现 2 个 5 ,每隔 125 个数,出现 3 个 5 ,以此类推。。。
最终出现 5 的个数就是 n / 5 + n / 25 + n / 125 ... 。
题解到这一步就能开始写程序了:
// 更加高效的计算因子 5
public int trailingZeroes_3(int n) {
int count = 0;
while (n > 0) {
n /= 5;
count += n;
}
return count;
}

终于出结果了,我太难了,不过 LeetCode 把这道题的难度定成简单真的合适么。。。
每日一道 LeetCode (41):阶乘后的零的更多相关文章
- LeetCode 172. 阶乘后的零(Factorial Trailing Zeroes)
172. 阶乘后的零 172. Factorial Trailing Zeroes 题目描述 给定一个整数 n,返回 n! 结果尾数中零的数量. LeetCode172. Factorial Trai ...
- Leetcode 172.阶乘后的零
阶乘后的零 给定一个整数 n,返回 n! 结果尾数中零的数量. 示例 1: 输入: 3 输出: 0 解释: 3! = 6, 尾数中没有零. 示例 2: 输入: 5 输出: 1 解释: 5! = 120 ...
- Java实现 LeetCode 172 阶乘后的零
172. 阶乘后的零 给定一个整数 n,返回 n! 结果尾数中零的数量. 示例 1: 输入: 3 输出: 0 解释: 3! = 6, 尾数中没有零. 示例 2: 输入: 5 输出: 1 解释: 5! ...
- 【每天一题】LeetCode 172. 阶乘后的零
开源地址:点击该链接 题目描述 https://leetcode-cn.com/problems/factorial-trailing-zeroes 给定一个整数 n,返回 n! 结果尾数中零的数量. ...
- 每日一道 LeetCode (3):回文数
前文合集 每日一道 LeetCode 文章合集 题目:回文数 题目来源:https://leetcode-cn.com/problems/palindrome-number/ 判断一个整数是否是回文数 ...
- 每日一道 LeetCode (5):最长公共前缀
前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee: https://gitee.com ...
- 每日一道 LeetCode (8):删除排序数组中的重复项和移除元素
每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ...
- 每日一道 LeetCode (14):数组加一
每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ...
- 每日一道 LeetCode (15):二进制求和
每天 3 分钟,走上算法的逆袭之路. 前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee ...
随机推荐
- Vue 给子组件绑定v-model
父组件使用子组件时,使用v-model指令,在子组件中使用value获取props的值 父组件 <template> <div style="margin:20px;dis ...
- neo4j批量导入数据的两种解决方案
neo4j批量导入数据有两种方法,第一种是使用cypher语法中的LOAD CSV,第二种是使用neo4j自带的工具neo4j-admin import. LOAD CSV 导入的文件必须是csv文件 ...
- HahMap(jdk=1.8)源码解读
简介:岁月磨平了人的棱角,让我们不敢轻易的去放手,即使它在你心中并不那么重要,你依旧害怕失去它,不是舍不得,是内心的迷茫. 一 : 创建HashMap HashMap<Object, Objec ...
- 根据pid获得路径 2006-10-25 19:28
这是编写kill process时用到的 BOOL GetProcessModule(DWORD dwPID, DWORD dwModuleID, LPMODULEENTRY32 lpMe32, DW ...
- 准确率99.9%的离线IP地址定位库
Ip2region是什么? ip2region - 准确率99.9%的离线IP地址定位库,0.0x毫秒级查询,ip2region.db数据库只有数MB,提供了java,php,c,python,nod ...
- JS 时间获取 (常用)
/** * 获取几天之前日期 */ daysAgo(dayNum = 0) { let myDate = new Date() let lw = new Date(myDate - 1000 * 60 ...
- 非确定性有穷状态决策自动机练习题Vol.2 C. 奇袭
非确定性有穷状态决策自动机练习题Vol.2 C. 奇袭 题目描述 由于各种原因,桐人现在被困在\(Under World\)(以下简称\(UW\))中,而\(UW\)马上 要迎来最终的压力测试--魔界 ...
- web主题适配方案指北
前置知识 在这里了解实现网页主题切换的相关知识. CSS 变量 要实现主题切换需要了解一点 css 自定义属性.当然,本文还提供了其他实现方式,为了不给您接下来的阅读带来阻碍,先了解它. 变量的声明 ...
- Typescript node starter 2.Router Middleware
Router 路由器对象是中间件和路由的一个独立实例.可以将它视为一个“迷你应用程序”,仅能够执行中间件和路由功能.每个Express应用程序都有一个内置的应用程序路由器. 路由器的行为类似于中间件本 ...
- RPC 框架通俗解释 转自知乎(洪春涛)
本地过程调用 RPC就是要像调用本地的函数一样去调远程函数.在研究RPC前,我们先看看本地调用是怎么调的.假设我们要调用函数Multiply来计算lvalue * rvalue的结果: 那么在第8行时 ...