每天学习一点算法 2026/094/14
题目:分数到小数
给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。
如果小数部分为循环小数,则将循环的部分括在括号内。
如果存在多个答案,只需返回 任意一个 。
对于所有给定的输入,保证 答案字符串的长度小于 104 。
注意,如果分数可以表示为有限长度的字符串,则 必须 返回它。
作者:LeetCode
链接:https://leetcode.cn/leetbook/read/top-interview-questions-medium/xwm8ne/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
首先我们要将整数部分和小数部分分开来处理
整数部分就是很简单,直接用分子除以分母向下取整即可,如果分子对分母取余为 0 表示没有小数,否则就要加上小数点。
然后就是小数部分,我们就需要一位一位的计算:
- 首先我们 n 设为分子对分母取余的结果
- 将 n 乘以 10,作为新的被除数
- 如果
n * 10大于等于分母:- 用他除以分母向下取整,得到的结果放在这一位上
n * 10对分母取余,结果为 0 表示计算结束,否则就要将 n 设为这次取余的结果继续重复步骤
- 如果
n * 10小于分母- 这一位为 0
- 设 n 为
n * 10继续重复步骤
如果结果没有循环小数就可以通过上面这种方法递归计算出结果,如果有循环的就会进入死循环,所以我们还需要处理循环小数的情况。
其实很容易就可以想到,当 n 重复的时候就代表计算的小数存在循环,我们可以用一个 set 记录出现的 n, 当 n 重复出现的时候就结束递归。具体实现代码如下:
typescript
function fractionToDecimal(numerator: number, denominator: number): string {
if (numerator === 0) return '0' // 分子为 0 结果为 0
// 依旧需要处理符号的问题
let flag = false
if ((numerator > 0 && denominator > 0) || (numerator < 0 && denominator < 0)) {
flag = true
}
numerator = Math.abs(numerator)
denominator = Math.abs(denominator)
let integer = '' // 整数部分
// 计算整数部分
const a = Math.floor(numerator / denominator)
integer += a
numerator %= denominator // 取余
if (numerator !== 0) integer += '.' // 判断结果是否为整数
const other: string[] = [] // 存放小数每一位的计算结果
const set = new Set() // 存储每次的被除数用于判断小数是否循环
function helper(n: number, m: number) {
if (n === 0) return // 设置 n 为 0 计算完毕
if (set.has(n)) {
// 设置边界开始循环
const setArr = Array.from(set)
const start = setArr.findIndex(item => item == n) // 找到循环开始的下标
// 添加循环括号
other.splice(start, 0, '(')
other.push(')')
return
}
set.add(n) // 添加被除数
if (n >= m) {
// 被除数大于等于分母
const a = Math.floor(n / m)
other.push(a.toString())
n %= m
} else {
// 被除数小于分母
other.push('0')
}
// 递归传递 n * 10
return helper(n * 10, m)
}
helper(numerator * 10, denominator) // 初始放入分子对分母取余的结果 * 10
return `${flag ? '' : '-'}${integer}${other.join('')}`
};
题目来源:力扣(LeetCode)