[230520 剑指43] 1~n 整数中 1 出现的次数
一 题目
输入一个整数 n
,求1~n这n个整数的十进制表示中1出现的次数。
例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
示例 1:
输入:n = 12
输出:5
示例 2:
输入:n = 13
输出:6
限制:
1 <= n < 2^31
二 整体思路
遍历肯定是超时的,所以我们采用逐位分析的思路。
逐位分析指的是:
- 固定当前位为1,找到最小的数和最大的数;
- 最小的数即为 只有当前位为1、其余位为 0 的数;
- 最大的数即为 当前位为 1 且不超过 n 的最大的数。
我们需要维护几个变量:high、low、cur、digit
然后分情况讨论:
- 若在 n 中,此位为 0,则只需根据高位来计算固定此位为 1 时整数个数
- 若在 n 中,此位为 1,则需要根据高位和低位来计算
- 若在 n 中,此位大于 1,则只需要根据高位来计算
当分析完当前位以后,迭代几个变量。
三 关键点/重点/难点
关键点在理解到固定当前位数字为 1 时,找到的最大的数是当前位为 1 且不超过 n 的最大的数。
并且要知道最小的数、最大的数和计算个数的关系,例如
分析十位:
2104:0010-2019 → 000-209 → 即209-000+1=210
分析百位:
2104:0100-2101 → 000-201 → 即201-000+1=202
分析千位:
12104:01000-11999 → 0000-1999 → 即1999-0000+1=2000
易错点在当 n 为 INT32_MAX 时,digit 会溢出,故需要把 digit 定义为 long 变量。
四 代码分析
class Solution {
public:
int countDigitOne(int n) {
//定义几个变量
int high = n / 10;
int cur = n % 10;
int low = 0;
long digit = 1;
//对n的各位进行拆分
//当high和cur都同时为0时,说明已经分析完了最高位
int result = 0;
while(high != 0 || cur != 0) {
//对digit位(即cur)进行分析
//2104:0010-2019 → 000-209 → 即209-000+1=210
//0010:digit位为1的最小的数
//2019:digit位为1的且不超过n的最大的数
if(cur == 0) {
result += high * digit;
}
//2104:0100-2101 → 000-201 → 即201-000+1=202
//202=2*100+1+1
else if(cur == 1) {
result += high * digit + low + 1;
}
//2-9
//12104:01000-11999 → 0000-1999 → 即1999-0000+1=2000
//2000=(1+1)*1000=2000
else {
result += (high + 1) * digit;
}
//迭代几个变量
low += cur * digit;
cur = high % 10;
high /= 10;
digit *= 10;
}
return result;
}
};
(五)一题多解
有递归和动态规划,懒得看了
(六) 知识扩展
C++ 中的 int 类型变量的所占内存大小
type | memory | min | max |
---|---|---|---|
int | 4 bytes | -2147483648 | 2147483647 |
long int | 4 bytes (16位、32位编译器) 8 bytes (64位编译器) | -2147483648 | 2147483647 |
long long int | 8 bytes |