233.数字 1 的个数
标签: math
, dynamic-programming
难度: Hard
通过率: 35.24%
原题链接: https://leetcode.com/problems/number-of-digit-one/description/
题目描述
给定一个整数 ,计算所有小于或等于 的非负整数中数字 1 出现的总次数。
解题思路
解决这个问题可以通过分析数字每一位上的“1”来完成。基本思想是从个位数开始到最高位,依次计算在每一位上的"1"的数量。设当前位记为 ,那我们要计算 位是 1 时,总的"1"的数量。
对于一个数字 ,我们拆分为三部分:
- 更高位数字:这些位大于当前位
- 当前位:正在分析的位
- 更低位数字:这些位小于当前位
假设当前位是第 位,我们可以通过整除和取模操作计算这三部分。
- 更高位:
- 当前位:
- 更低位:
然后,我们分析 位上的"1"的情况:
- 如果 ,那 么当前位的"1"可以出现在 每个数字组合中,即 次。
- 如果 ,那就在 之间多出 个组合可以出现"1"。
- 如果 ,此位上不存在"1",总可能数就是 次。
我们迭代计算从个位到最高位,把每一位的"1"的数量累加即可得到最终的结果。
代码实现
- Python
- C++
- JavaScript
- Java
def countDigitOne(n):
# 初始化位置和结果数量
i = 1 # 用于定位当前分析的位数
count = 0 # 最终计数结果
# 逐位解析,直到超过数字n
while i <= n:
# 计算高位、当前位和低位的值
high = n // (i * 10)
curr = (n // i) % 10
low = n % i
if curr == 0:
# 如果当前位为0,说明当前位没有1
count += high * i
elif curr == 1:
# 当前位为1,计数包含低位变化
count += high * i + (low + 1)
else:
# 当前位大于1,包含完全变化可能
count += (high + 1) * i
# 左移至下一位
i *= 10
return count
int countDigitOne(int n) {
long long i = 1; //起始位
int count = 0; //储存计数的结果
while (i <= n) {
long long high = n / (i * 10);
int curr = (n / i) % 10;
int low = n % i;
if (curr == 0) {
count += high * i;
} else if (curr == 1) {
count += high * i + low + 1;
} else {
count += (high + 1) * i;
}
i *= 10;
}
return count;
}
function countDigitOne(n) {
let count = 0;
let i = 1;
while (i <= n) {
const high = Math.floor(n / (i * 10));
const curr = Math.floor(n / i) % 10;
const low = n % i;
if (curr === 0) {
count += high * i;
} else if (curr === 1) {
count += high * i + low + 1;
} else {
count += (high + 1) * i;
}
i *= 10;
}
return count;
}
public int countDigitOne(int n) {
long i = 1; // 用于定位当前位数
int count = 0; // 记录计数
while (i <= n) {
long high = n / (i * 10);
int curr = (int)((n / i) % 10);
int low = (int)(n % i);
if (curr == 0) {
count += high * i;
} else if (curr == 1) {
count += high * i + low + 1;
} else {
count += (high + 1) * i;
}
i *= 10; // 乘10以处理下一位
}
return count;
}
复杂度分析
时间复杂度为 ,因为我们是逐位分析数字 ,而 中的位数最多是 。
空间复杂度为 ,因为我们只用了常数级别的额外空间来跟踪状态和计算。