LeetCode/solutions/233. Number of Digit One.md
2020-02-10 15:34:42 +08:00

84 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# [233. Number of Digit One](https://leetcode.com/problems/number-of-digit-one/)
# 思路
给定整数n求1~n这n个数中数字1出现的个数例如 n = 2345的结果可以根据 n = 345 和 n = 999 来求得。
## 思路一
比较好想的是递归法,核心思想就是每次去掉最高位。
* 递归出口:当`n < 1`时直接返回0
* 否则我们假设n表示成"aB"的形式其中a代表n的最高位B代表其余位例如若 n = 2345 则a=2B=345。我们还令M为与n位数相同的最小的数即若 n=2345 M = 1000。所以很明显我们有等式`n = a * M + B`。
* 先考虑出现在最高位的1的个数所以这里需要考虑a是否为1若是则有B+1个否则有M个例如若n = 1234那么千位的1就有235个即1000~1234若n = 2234则有1000个即1000~1999)。
* 再考虑非最高位的1的个数0到`a*M`中出现在非最高位的1的个数为`a*countDigitOne(M-1)`例如0~3000中出现在非最高位即个十百位的1的个数为`3*countDigitOne(999)``a*M+1``a*M+B`中出现在非最高位的1的个数为`a*countDigitOne(B)`例如3001到3421中出现在非最高位即个十百位的1的个数为`countDigitOne(421)`。
* 综上总的1的个数为`a * countDigitOne(M - 1) + countDigitOne(B) + (a == 1 ? B + 1 : M)`。
每次递归都去掉了最高位所以递归次数和位数相同即时间复杂度为O(logn)空间复杂度O(logn)。
## 思路二
再来看看迭代的思路
核心思想就是统计出每位个十百千...上1出现的个数累加起来就是1出现的总个数
例如现在统计百位上1出现的次数
* 先将n根据百位分成两部分`n = 100*a + b`。
* 此时a的个位的值有三种情况
* a的个位大于1例如 n = 31456 a = 314a的个位是4。此时1-n的百位是1的次数为`32*100`即形为A1BA为0-31B为0-99
* a的个位等于1即n的百位是1例如 n = 31156 a = 311。此时0-n的百位是1的次数为`31*100 + 56 + 1`即形为A1BA为0-30B为0-99或者311CC为0到56
* a的个位等于0即n的百位是1例如 n = 31056 a = 310。此时0-n的百位是1的次数为`31*100`即形为A1BA为0-30B为0-99
|n的百位| 0到n的百位是1的次数 | n举例 | a | b | 0到n的百位是1的次数 |
|--- | --- | --- | --- | --- | --- |
| 大于1 | `(a/10 + 1) * 100` | 31456 | 314 | 56 | `32*100` |
| 等于1 | `(a/10) * 100 + b + 1` | 31156 | 311 | 56 | `31*100 + 56 + 1` |
| 等于0 | `(a/10) * 100` | 31056 | 310 | 56 | `31*100` |
上述三个情况可以用一个表达式写出
```
(a+8)/10 * 100 + (a % 10 == 1) * (b + 1)
```
有了上述分析我们就可以写出代码了注意避免溢出
时间复杂度为O(logn)空间复杂度O(1)。
# C++
## 思路一
``` C++
class Solution {
public:
int countDigitOne(int n) {
if(n < 1) return 0;
// else if(n < 10) return 1;
// n = 2345 -> a=2, M=1000, B=345
int a = n, M = 1, B = 0;
while(a >= 10){
a /= 10;
M *= 10;
}
B = n % M;
int res = a * countDigitOne(M - 1) + countDigitOne(B); //非最高位的1的个数
res += (a == 1 ? B + 1 : M); //最高位的1的个数
return res;
}
};
```
## 思路二
``` C++
class Solution {
public:
int countDigitOne(int n) {
int ones = 0;
for (long long m = 1; m <= n; m *= 10) {
int a = n / m, b = n % m;
ones += (a + 8) / 10 * m + (a % 10 == 1) * (b + 1);
}
return ones;
}
};
```