在本篇文章中,我们将详细解读力扣第233题“数字1的个数”。通过学习本篇文章,读者将掌握如何计算1到n之间数字中“1”出现的次数,并了解相关的复杂度分析和模拟面试问答。每种方法都将配以详细的解释,以便于理解。
问题描述
力扣第233题“数字1的个数”描述如下:
给定一个整数
n
,计算从 1 到n
的所有整数中数字1
出现的次数。示例:
输入: n = 13 输出: 6 解释: 1~13中共出现了6个数字1:1, 10, 11, 12, 13
示例:
输入: n = 100 输出: 21
解题思路
方法一:逐位分析法
-
初步分析:
- 我们可以通过逐位分析的方法,逐位计算数字1在每个数位上出现的次数。假设我们需要计算从1到n中1在第
d
位(从低位到高位)上出现的次数,我们可以通过分析每个数字在当前位的前缀、当前位以及后缀来进行计算。
- 我们可以通过逐位分析的方法,逐位计算数字1在每个数位上出现的次数。假设我们需要计算从1到n中1在第
-
步骤:
- 初始化
count
为 0,表示数字1的总次数。 - 对于每一位,计算当前位的前缀和后缀,并根据当前位的值判断数字1出现的次数。
- 如果当前位是 0,1 的出现次数由前缀决定。
- 如果当前位是 1,1 的出现次数由前缀和后缀共同决定。
- 如果当前位大于 1,1 的出现次数由前缀加上所有低位的可能数决定。
- 初始化
代码实现
def countDigitOne(n: int) -> int:
count = 0
i = 1
while i <= n:
divider = i * 10
count += (n // divider) * i + min(max(n % divider - i + 1, 0), i)
i *= 10
return count
# 测试案例
print(countDigitOne(13)) # 输出: 6
print(countDigitOne(100)) # 输出: 21
方法二:枚举法
-
初步分析:
- 可以通过直接枚举每个数字,然后遍历每个数字中的每一位来统计“1”的出现次数。虽然这种方法相对简单,但效率较低,适合小规模数据。
-
步骤:
- 初始化
count
为 0。 - 遍历从 1 到 n 的每个数字,对于每个数字逐位检查是否为 1,如果是则增加计数。
- 返回
count
。
- 初始化
代码实现
def countDigitOne(n: int) -> int:
count = 0
for i in range(1, n + 1):
count += str(i).count('1')
return count
# 测试案例
print(countDigitOne(13)) # 输出: 6
print(countDigitOne(100)) # 输出: 21
复杂度分析
-
时间复杂度:
- 逐位分析法:O(log(n)),因为每次迭代处理一位,最多处理 log(n) 位数字。
- 枚举法:O(n * log(n)),需要遍历从1到n的每个数字,并检查每个数字中的每一位。
-
空间复杂度:
- 两种方法的空间复杂度都是 O(1),因为只使用了少量额外的变量。
模拟面试问答
问题 1:你能描述一下如何解决这个问题的思路吗?
回答:我们可以通过逐位分析法来解决这个问题。逐位分析法通过分析每一位上数字“1”的出现情况,逐位计算1在各个位数上出现的次数。具体来说,通过分析每一位的前缀、当前位以及后缀,可以计算出数字“1”在这一位上出现的次数,并累加到总次数中。
问题 2:为什么选择使用逐位分析法来解决这个问题?
回答:逐位分析法在解决这个问题上非常高效。通过逐位分析,我们可以在 O(log(n)) 的时间复杂度内计算出1到n中数字“1”的总出现次数,而不用遍历所有的数字。相比枚举法,逐位分析法的效率更高,适合处理大规模数据。
问题 3:你的算法的时间复杂度和空间复杂度是多少?
回答:逐位分析法的时间复杂度是 O(log(n)),因为算法需要对每一位进行处理,最多处理 log(n) 位数字。空间复杂度为 O(1),只使用了少量的额外变量。枚举法的时间复杂度是 O(n * log(n)),因为需要遍历每个数字并检查每个数字中的每一位。
问题 4:在代码中如何处理边界情况?
回答:对于 n 小于 1 的情况,直接返回 0,因为没有数字可以统计。对于大数,我们通过逐位分析法逐位处理,确保不会因数字过大而导致溢出或计算错误。通过处理前缀、当前位和后缀的逻辑,可以应对各种输入情况。
问题 5:你能解释一下逐位分析法在这个问题中的具体作用吗?
回答:逐位分析法通过逐位检查每一位数字1的出现次数来计算总的出现次数。它通过考虑每一位的前缀、当前位和后缀,分别计算在不同情况下数字1的出现次数,并通过这种方法将复杂问题分解为多个简单问题。最终,通过将每一位的出现次数累加,得到总次数。
问题 6:在代码中如何确保返回的结果是正确的?
回答:通过逐位处理数字1的出现次数,确保每一位的结果都正确累加到总次数中。同时,通过验证不同情况下前缀、当前位和后缀的计算,确保计算逻辑的正确性。通过测试用例验证,确保代码能够正确处理各种输入情况。
问题 7:你能举例说明在面试中如何回答优化问题吗?
回答:在面试中,如果被问到如何优化算法,我会首先分析当前算法的时间复杂度和空间复杂度。由于逐位分析法的时间复杂度已经是 O(log(n)),没有进一步优化的空间,但可以讨论如何减少代码的复杂性或提高代码的可读性。此外,也可以讨论在特定场景下是否有其他更优的算法或数据结构可以使用。
问题 8:如何验证代码的正确性?
回答:通过编写详细的测试用例,涵盖各种可能的输入情况,如 1、10、100、1000 等典型的数字,确保每个测试用例的结果都符合预期。此外,还可以通过手工计算和推演来验证逐位分析法的逻辑,确保代码的正确性。
问题 9:你能解释一下解决“数字1的个数”问题的重要性吗?
回答:解决“数字1的个数”问题展示了对数字和位运算的理解。这种技巧在处理大数问题、位运算问题等场景中非常有用。通过掌握逐位分析法,可以提高对数字处理的理解,并为解决更复杂的数字问题打下基础。
问题 10:在处理大数据集时,算法的性能如何?
回答:逐位分析法在处理大数据集时表现良好,因为它的时间复杂度为 O(log(n)),对于大数也能高效处理。相比枚举法,逐位分析法能够在线性时间内处理大规模数据,适合在实际应用中进行优化。
总结
本文详细解读了力扣第233题“数字1的个数”,通过逐位分析法和枚举法计算1到n之间数字“1”的总出现次数,并提供了详细的解释和模拟面试问答。希望读者通过本文的学习,能够在力扣刷题的过程中更加得心应手。