Skip to content

Commit 9cecb15

Browse files
committed
233
1 parent d6623e5 commit 9cecb15

File tree

3 files changed

+178
-1
lines changed

3 files changed

+178
-1
lines changed

SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,4 +212,5 @@
212212
* [230. Kth Smallest Element in a BST](leetcode-230-Kth-Smallest-Element-in-a-BST.md)
213213
* [231*. Power of Two](leetcode-231-Power-of-Two.md)
214214
* [232. Implement Queue using Stacks](leetcode-232-Implement-Queue-using-Stacks.md)
215+
* [233. Number of Digit One](leetcode-233-Number-of-Digit-One.md)
215216
* [更多](more.md)

leetcode-201-300.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,6 @@
6262

6363
<a href="leetcode-231-Power-of-Two.html">231. Power of Two</a>
6464

65-
<a href="leetcode-232-Implement-Queue-using-Stacks.html">232. Implement Queue using Stacks</a>
65+
<a href="leetcode-232-Implement-Queue-using-Stacks.html">232. Implement Queue using Stacks</a>
66+
67+
<a href="leetcode-233-Number-of-Digit-One.html">233. Number of Digit One</a>
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# 题目描述(困难难度)
2+
3+
![](https://windliang.oss-cn-beijing.aliyuncs.com/233.jpg)
4+
5+
给一个数 `n`,输出 `0 ~ n` 中的数字中 `1` 出现的个数。
6+
7+
# 解法一 暴力
8+
9+
直接想到的当然就是暴力的方法,一个数一个数的判断,一位一位的判断。
10+
11+
```java
12+
public int countDigitOne(int n) {
13+
int num = 0;
14+
for (int i = 1; i <= n; i++) {
15+
int temp = i;
16+
while (temp > 0) {
17+
if (temp % 10 == 1) {
18+
num++;
19+
}
20+
temp /= 10;
21+
}
22+
}
23+
return num;
24+
}
25+
```
26+
27+
但这个解法会超时。
28+
29+
# 解法二
30+
31+
自己也没想到别的方法,讲一下 [这里](https://leetcode.com/problems/number-of-digit-one/discuss/64382/JavaPython-one-pass-solution-easy-to-understand) 的思路。
32+
33+
总体思想就是分类,先求所有数中个位是 `1` 的个数,再求十位是 `1` 的个数,再求百位是 `1` 的个数...
34+
35+
假设 `n = xyzdabc`,此时我们求千位是 `1` 的个数,也就是 `d` 所在的位置。
36+
37+
那么此时有三种情况,
38+
39+
* `d == 0`,那么千位上 `1` 的个数就是 `xyz * 1000`
40+
* `d == 1`,那么千位上 `1` 的个数就是 `xyz * 1000 + abc + 1`
41+
* `d > 1`,那么千位上 `1` 的个数就是 `xyz * 1000 + 1000`
42+
43+
为什么呢?
44+
45+
当我们考虑千位是 `1` 的时候,我们将千位定为 `1`,也就是 `xyz1abc`
46+
47+
对于 `xyz` 的话,可以取 `0,1,2...(xyz-1)`,也就是 `xyz` 种可能。
48+
49+
`xyz` 固定为上边其中的一个数的时候,`abc` 可以取 `0,1,2...999`,也就是 `1000` 种可能。
50+
51+
这样的话,总共就是 `xyz*1000` 种可能。
52+
53+
注意到,我们前三位只取到了 `xyz-1`,那么如果取 `xyz` 呢?
54+
55+
此时就出现了上边的三种情况,取决于 `d` 的值。
56+
57+
`d == 1` 的时候,千位刚好是 `1`,此时 `abc` 可以取的值就是 `0``abc` ,所以多加了 `abc + 1`
58+
59+
`d > 1` 的时候,`d` 如果取 `1`,那么 `abc` 就可以取 `0``999`,此时就多加了 `1000`
60+
61+
再看一个具体的例子。
62+
63+
```java
64+
如果n = 4560234
65+
让我们统计一下千位有多少个 1
66+
xyz 可以取 0455, abc 可以取 0999
67+
4551000 to 4551999 (1000)
68+
4541000 to 4541999 (1000)
69+
4531000 to 4531999 (1000)
70+
...
71+
21000 to 21999 (1000)
72+
11000 to 11999 (1000)
73+
1000 to 1999 (1000)
74+
总共就是 456 * 1000
75+
76+
如果 n = 4561234
77+
xyz 可以取 0455, abc 可以取 0999
78+
4551000 to 4551999 (1000)
79+
4541000 to 4541999 (1000)
80+
4531000 to 4531999 (1000)
81+
...
82+
1000 to 1999 (1000)
83+
xyz 还可以取 456, abc 可以取 0234
84+
4561000 to 4561234 (234 + 1)
85+
总共就是 456 * 1000 + 234 + 1
86+
87+
如果 n = 4563234
88+
xyz 可以取 0455, abc 可以取 0999
89+
4551000 to 4551999 (1000)
90+
4541000 to 4541999 (1000)
91+
4531000 to 4531999 (1000)
92+
...
93+
1000 to 1999 (1000)
94+
xyz 还可以取 456, abc 可以取 0999
95+
4561000 to 4561999 (1000)
96+
总共就是 456 * 1000 + 1000
97+
```
98+
99+
至于其它位的话是一样的道理。
100+
101+
代码的话就很好写了。
102+
103+
```java
104+
public int countDigitOne(int n) {
105+
int count = 0;
106+
//依次考虑个位、十位、百位...是 1
107+
//k = 1000, 对应于上边举的例子
108+
for (int k = 1; k <= n; k *= 10) {
109+
// xyzdabc
110+
int abc = n % k;
111+
int xyzd = n / k;
112+
int d = xyzd % 10;
113+
int xyz = xyzd / 10;
114+
count += xyz * k;
115+
if (d > 1) {
116+
count += k;
117+
}
118+
if (d == 1) {
119+
count += abc + 1;
120+
}
121+
//如果不加这句的话,虽然 k 一直乘以 10,但由于溢出的问题
122+
//k 本来要大于 n 的时候,却小于了 n 会再次进入循环
123+
//此时代表最高位是 1 的情况也考虑完成了
124+
if(xyz == 0){
125+
break;
126+
}
127+
}
128+
return count;
129+
}
130+
```
131+
132+
然后代码的话,可以再简化一下,注意到 `d > 1` 的时候,我们多加了一个 `k`
133+
134+
我们可以通过计算 `long xyz = xyzd / 10;` 的时候,给 `xyzd` 多加 `8`,从而使得当 `d > 1` 的时候,此时求出来的 `xyz` 会比之前大 `1`,这样计算 `xyz * k` 的时候就相当于多加了一个 `k`
135+
136+
此外,上边 `k` 溢出的问题,我们可以通过 `long` 类型解决。
137+
138+
```java
139+
public int countDigitOne(int n) {
140+
int count = 0;
141+
for (long k = 1; k <= n; k *= 10) {
142+
// xyzdabc
143+
int abc = n % k;
144+
int xyzd = n / k;
145+
int d = xyzd % 10;
146+
int xyz = (xyzd + 8) / 10;
147+
count += xyz * k;
148+
if (d == 1) {
149+
count += abc + 1;
150+
}
151+
}
152+
return count;
153+
}
154+
```
155+
156+
而这个代码,其实和 Solution [高赞](https://leetcode.com/problems/number-of-digit-one/discuss/64381/4%2B-lines-O(log-n)-C%2B%2BJavaPython) 中的解法是一样的,只不过省去了 `xyz``d` 这两个变量。
157+
158+
```java
159+
public int countDigitOne(int n) {
160+
int count = 0;
161+
162+
for (long k = 1; k <= n; k *= 10) {
163+
long r = n / k, m = n % k;
164+
// sum up the count of ones on every place k
165+
count += (r + 8) / 10 * k + (r % 10 == 1 ? m + 1 : 0);
166+
}
167+
168+
return count;
169+
}
170+
```
171+
172+
#
173+
174+
这道题的话,就是数学的分类、找规律的题目了,和 [172 题](https://leetcode.wang/leetcode-172-Factorial-Trailing-Zeroes.html) 找阶乘中 `0` 的个数一样,没有一些通用的算法,完全靠分析能力,如果面试碰到很容易卡主。

0 commit comments

Comments
 (0)