【问题描述】[困难]
【解答思路】
1. 暴力 (超时)
逐个数统计’1’的数量
时间复杂度:O(N^2) 空间复杂度:O(1)
public int countDigitOne(int n) {
int count = 0;
for(int i=1;i<=n;i++){
int tmp = i;
while(tmp>0){
if(tmp%10==1){
count++;
}
tmp/=10;
}
}
return count;
}
2. 分类
思路一
如果n = 4560234
让我们统计一下千位有多少个 1
xyz 可以取 0 到 455, abc 可以取 0 到 999
4551000 to 4551999 (1000)
4541000 to 4541999 (1000)
4531000 to 4531999 (1000)
…
21000 to 21999 (1000)
11000 to 11999 (1000)
1000 to 1999 (1000)
总共就是 456 * 1000
如果 n = 4561234
xyz 可以取 0 到 455, abc 可以取 0 到 999
4551000 to 4551999 (1000)
4541000 to 4541999 (1000)
4531000 to 4531999 (1000)
…
1000 to 1999 (1000)
xyz 还可以取 456, abc 可以取 0 到 234
4561000 to 4561234 (234 + 1)
总共就是 456 * 1000 + 234 + 1
如果 n = 4563234
xyz 可以取 0 到 455, abc 可以取 0 到 999
4551000 to 4551999 (1000)
4541000 to 4541999 (1000)
4531000 to 4531999 (1000)
…
1000 to 1999 (1000)
xyz 还可以取 456, abc 可以取 0 到 999
4561000 to 4561999 (1000)
总共就是 456 * 1000 + 1000
public int countDigitOne(int n) {
int count = 0;
//依次考虑个位、十位、百位...是 1
//k = 1000, 对应于上边举的例子
for (int k = 1; k <= n; k *= 10) {
// xyzdabc
int abc = n % k;
int xyzd = n / k;
int d = xyzd % 10;
int xyz = xyzd / 10;
count += xyz * k;
if (d > 1) {
count += k;
}
if (d == 1) {
count += abc + 1;
}
//如果不加这句的话,虽然 k 一直乘以 10,但由于溢出的问题
//k 本来要大于 n 的时候,却小于了 n 会再次进入循环
//此时代表最高位是 1 的情况也考虑完成了
if(xyz == 0){
break;
}
}
return count;
}
思路二
时间复杂度:O(logN) 空间复杂度:O(1)
public int countDigitOne(int n) {
int digit = 1, res = 0;
int high = n / 10, cur = n % 10, low = 0;
while(high != 0 || cur != 0) {
if(cur == 0) res += high * digit;
else if(cur == 1) res += high * digit + low + 1;
else res += (high + 1) * digit;
low += cur * digit;
cur = high % 10;
high /= 10;
digit *= 10;
}
return res;
}
3. 递归
class Solution {
public int countDigitOne(int n) {
return f(n);
}
private int f(int n ) {
if (n <= 0)
return 0;
String s = String.valueOf(n);
int high = s.charAt(0) - '0';
int pow = (int) Math.pow(10, s.length()-1);
int last = n - high*pow;
if (high == 1) {
return f(pow-1) + last + 1 + f(last);
} else {
return pow + high*f(pow-1) + f(last);
}
}
}
【总结】
1.这道题属于数学的分类,需要找规律,没有一些通用的算法,完全靠分析能力,如果面试碰到很容易卡住。
看实例理解
2.细心分析 注意边界
参考链接:https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/solution/mian-shi-ti-43-1n-zheng-shu-zhong-1-chu-xian-de-2/
参考链接:https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/solution/javadi-gui-by-xujunyi/
参考链接:https://leetcode-cn.com/problems/number-of-digit-one/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-50/