深入解析力扣166题:分数到小数(模拟长除法与字符串操作详解及模拟面试问答)

简介: 深入解析力扣166题:分数到小数(模拟长除法与字符串操作详解及模拟面试问答)

力扣166题:分数到小数

在本篇文章中,我们将详细解读力扣第166题“分数到小数”。通过学习本篇文章,读者将掌握如何使用多种方法来解决这一问题,并了解相关的复杂度分析和模拟面试问答。每种方法都将配以详细的解释和ASCII图解,以便于理解。

问题描述

力扣第166题“分数到小数”描述如下:

给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以字符串形式返回小数。如果小数部分是循环小数,则将循环部分括在括号内。

示例 1:

输入: numerator = 1, denominator = 2
输出: "0.5"

示例 2:

输入: numerator = 2, denominator = 1
输出: "2"

示例 3:

输入: numerator = 2, denominator = 3
输出: "0.(6)"

解题思路

方法一:模拟长除法
  1. 初步分析
  • 使用长除法模拟计算小数部分,并记录每一步余数的位置来检测循环。
  • 使用哈希表存储余数的位置,如果发现重复的余数,则表示循环节开始。
  1. 步骤
  • 处理分子和分母的符号,计算整数部分。
  • 模拟长除法计算小数部分,记录每一步余数的位置。
  • 如果发现重复的余数,则将循环部分括在括号内。
代码实现
def fractionToDecimal(numerator, denominator):
    if numerator == 0:
        return "0"
    result = []
    if (numerator < 0) ^ (denominator < 0):
        result.append("-")
    numerator, denominator = abs(numerator), abs(denominator)
    result.append(str(numerator // denominator))
    remainder = numerator % denominator
    if remainder == 0:
        return "".join(result)
    result.append(".")
    remainders = {}
    while remainder != 0:
        if remainder in remainders:
            result.insert(remainders[remainder], "(")
            result.append(")")
            break
        remainders[remainder] = len(result)
        remainder *= 10
        result.append(str(remainder // denominator))
        remainder %= denominator
    return "".join(result)
# 测试案例
print(fractionToDecimal(1, 2))  # 输出: "0.5"
print(fractionToDecimal(2, 1))  # 输出: "2"
print(fractionToDecimal(2, 3))  # 输出: "0.(6)"
ASCII图解

假设输入为 numerator = 2denominator = 3,图解如下:

2 ÷ 3 = 0.6666...
步骤:
整数部分: 0
小数部分:
2 * 10 = 20, 20 ÷ 3 = 6, 余数 = 2
2 * 10 = 20, 20 ÷ 3 = 6, 余数 = 2 (循环开始)
结果:0.(6)
方法二:通过字符串操作
  1. 初步分析
  • 直接通过字符串操作来构建结果字符串。
  • 使用哈希表记录每个余数出现的位置,检测循环节。
  1. 步骤
  • 处理分子和分母的符号,计算整数部分。
  • 使用字符串操作计算小数部分,记录每个余数的位置。
  • 如果发现重复的余数,则将循环部分括在括号内。
代码实现
def fractionToDecimal(numerator, denominator):
    if numerator == 0:
        return "0"
    sign = "-" if (numerator < 0) ^ (denominator < 0) else ""
    numerator, denominator = abs(numerator), abs(denominator)
    integer_part = str(numerator // denominator)
    remainder = numerator % denominator
    if remainder == 0:
        return sign + integer_part
    
    result = [sign + integer_part + "."]
    remainder_map = {}
    while remainder != 0:
        if remainder in remainder_map:
            result.insert(remainder_map[remainder], "(")
            result.append(")")
            break
        remainder_map[remainder] = len(result)
        remainder *= 10
        result.append(str(remainder // denominator))
        remainder %= denominator
    return "".join(result)
# 测试案例
print(fractionToDecimal(1, 2))  # 输出: "0.5"
print(fractionToDecimal(2, 1))  # 输出: "2"
print(fractionToDecimal(2, 3))  # 输出: "0.(6)"

复杂度分析

  • 时间复杂度
  • 模拟长除法方法:O(n),其中 n 是小数部分的长度,主要消耗在余数的检测和计算上。
  • 字符串操作方法:O(n),其中 n 是小数部分的长度,主要消耗在字符串构建和余数检测上。
  • 空间复杂度
  • 模拟长除法方法:O(n),需要额外的哈希表空间来存储余数的位置。
  • 字符串操作方法:O(n),需要额外的哈希表空间来存储余数的位置。

模拟面试问答

问题 1:你能描述一下如何解决这个问题的思路吗?

回答:我们需要将分数转换为小数,并检测是否存在循环小数。可以使用模拟长除法的方法,记录每一步余数的位置来检测循环。如果发现重复的余数,则表示循环节开始,将循环部分括在括号内。另一种方法是通过字符串操作直接构建结果,使用哈希表记录每个余数出现的位置,检测循环节。

问题 2:为什么要使用哈希表记录余数的位置?

回答:使用哈希表记录余数的位置可以有效检测循环小数。当发现重复的余数时,表示开始出现循环节,可以将循环部分括在括号内。这种方法可以快速确定循环节的起始位置和结束位置。

问题 3:你的算法的时间复杂度和空间复杂度是多少?

回答:模拟长除法方法的时间复杂度是 O(n),空间复杂度是 O(n),其中 n 是小数部分的长度。字符串操作方法的时间复杂度是 O(n),空间复杂度是 O(n)。

问题 4:在什么情况下会出现循环小数?

回答:当分子和分母的最大公约数不为1时,且分母有质因数2或5之外的其他质因数时,分数转换为小数会出现循环小数。例如,2/3转换为小数时,会出现循环小数0.(6)。

问题 5:你能解释一下模拟长除法的方法吗?

回答:模拟长除法的方法通过逐步计算小数部分,每一步记录当前的余数位置。如果发现重复的余数,则表示开始出现循环节。将循环部分括在括号内,生成最终结果。

问题 6:如何处理分子或分母为负数的情况?

回答:首先判断分子和分母的符号,通过异或运算确定结果的符号。然后将分子和分母转换为正数,继续进行后续计算。最后将符号添加到结果字符串的开头。

问题 7:在代码中如何处理分数的整数部分和小数部分?

回答:首先计算整数部分,将整数部分添加到结果字符串中。然后计算小数部分,通过模拟长除法或字符串操作的方法逐步计算,检测循环节并构建最终结果。

问题 8:你能举例说明在面试中如何回答优化问题吗?

回答:在面试中,如果面试官问到如何优化算法,我会首先分析当前算法的瓶颈,如时间复杂度和空间复杂度,然后提出优化方案。例如,对于分数到小数问题,我会提到使用哈希表记录余数的位置,以快速检测循环节,并解释其原理和优势,最后提供代码实现和复杂度分析。

问题 9:如何验证代码的正确性?

回答:通过多个测试案例验证代码的正确性,包括正常情况和边界情况。例如,测试分数的整数部分和小数部分、前导零情况、循环小数情况等,确保代码在各种情况下都能正确运行。

问题 10:你能解释一下分数到小数转换的重要性吗?

回答:分数到小数的转换在数学计算、科学研究和工程应用中非常重要。例如,精确表示和计算分数值,确保计算结果的准确性。分数到小数的转换还用于金融和统计分析中,帮助人们更直观地理解和比较数值大小。

总结

本文详细解读了力扣第166题“分数到小数”,通过模拟长除法和字符串操作两种方法,高效地解决了这一问题,并提供了详细的ASCII图解和模拟面试问答。希望读者通过本文的学习,能够在力扣刷题的过程中更加得心应手。

参考资料

  • 《算法导论》—— Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein
  • 力扣官方题解

欢迎关注微信公众号 数据分析螺丝钉

相关文章
|
5月前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
10月前
|
存储 算法 Go
【LeetCode 热题100】17:电话号码的字母组合(详细解析)(Go语言版)
LeetCode 17题解题思路采用回溯算法,通过递归构建所有可能的组合。关键点包括:每位数字对应多个字母,依次尝试;递归构建下一个字符;递归出口为组合长度等于输入数字长度。Go语言实现中,使用map存储数字到字母的映射,通过回溯函数递归生成组合。时间复杂度为O(3^n * 4^m),空间复杂度为O(n)。类似题目包括括号生成、组合、全排列等。掌握回溯法的核心思想,能够解决多种排列组合问题。
412 11
|
10月前
|
Go
【LeetCode 热题100】155:最小栈(详细解析)(Go语言版)
本文详细解析了力扣热题155:最小栈的解题思路与实现方法。题目要求设计一个支持 push、核心思路是使用辅助栈法,通过两个栈(主栈和辅助栈)来维护当前栈中的最小值。具体操作包括:push 时同步更新辅助栈,pop 时检查是否需要弹出辅助栈的栈顶,getMin 时直接返回辅助栈的栈顶。文章还提供了 Go 语言的实现代码,并对复杂度进行了分析。此外,还介绍了单栈 + 差值记录法的进阶思路,并总结了常见易错点,如 pop 操作时忘记同步弹出辅助栈等。
340 6
|
10月前
|
Go 索引
【LeetCode 热题100】739:每日温度(详细解析)(Go语言版)
这篇文章详细解析了 LeetCode 第 739 题“每日温度”,探讨了如何通过单调栈高效解决问题。题目要求根据每日温度数组,计算出等待更高温度的天数。文中推荐使用单调递减栈,时间复杂度为 O(n),优于暴力解法的 O(n²)。通过实例模拟和代码实现(如 Go 语言版本),清晰展示了栈的操作逻辑。此外,还提供了思维拓展及相关题目推荐,帮助深入理解单调栈的应用场景。
389 6
|
11月前
|
存储 算法 数据可视化
【二叉树遍历入门:从中序遍历到层序与右视图】【LeetCode 热题100】94:二叉树的中序遍历、102:二叉树的层序遍历、199:二叉树的右视图(详细解析)(Go语言版)
本文详细解析了二叉树的三种经典遍历方式:中序遍历(94题)、层序遍历(102题)和右视图(199题)。通过递归与迭代实现中序遍历,深入理解深度优先搜索(DFS);借助队列完成层序遍历和右视图,掌握广度优先搜索(BFS)。文章对比DFS与BFS的思维方式,总结不同遍历的应用场景,为后续构造树结构奠定基础。
528 10
|
11月前
|
Go 索引 Perl
【LeetCode 热题100】【二叉树构造题精讲:前序 + 中序建树 & 有序数组构造 BST】(详细解析)(Go语言版)
本文详细解析了二叉树构造的两类经典问题:通过前序与中序遍历重建二叉树(LeetCode 105),以及将有序数组转化为平衡二叉搜索树(BST,LeetCode 108)。文章从核心思路、递归解法到实现细节逐一拆解,强调通过索引控制子树范围以优化性能,并对比两题的不同构造逻辑。最后总结通用构造套路,提供进阶思考方向,帮助彻底掌握二叉树构造类题目。
705 9
|
11月前
|
算法 Go
【LeetCode 热题100】73:矩阵置零(详细解析)(Go语言版)
这篇文章详细解析了力扣热题 73——矩阵置零问题,提供两种解法:一是使用额外标记数组,时间复杂度为 O(m * n),空间复杂度为 O(m + n);二是优化后的原地标记方法,利用矩阵的第一行和第一列记录需要置零的信息,将空间复杂度降低到 O(1)。文章通过清晰的代码示例与复杂度分析,帮助理解“原地操作”及空间优化技巧,并推荐相关练习题以巩固矩阵操作能力。适合刷题提升算法思维!
340 9
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
357 6
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
206 6
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
453 2

推荐镜像

更多
  • DNS