问题来源
https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/
暴力法
对于暴力的方法,只需要从1开始,对1 -> n
中的所有数进行枚举,然后依次判断该数的每一位(个位,十位,百位 .... ....)
是否为1
- 如果该位是1,计数器
ans++
- 如果该位不是1,继续判断下一位
int countDigitOne(int n) {
int ans = 0;
for(int i = 1; i <= n; i++)
{
int num = i;
while(num)
{
if(num % 10 == 1)
ans++;
num /= 10;
}
}
return ans;
}
当数据量特别大的时候,最后的结果是会超时的
优化后的写法
既然暴力的方法不可以,那么我们是否可以根据当前位(个位,十位,百位 .... ....)
的数字,然后确定出以该位为中心,一共有多少个1的情况。
对于这种做法,我们可以把一个数字(n n-1 … … 1)分成三部分,设当前位为第 x 位:
- 高位,n - > x + 1 位,也就是 n n-1 … … x + 1
- 当前位,第 x 位,也就是 x
- 低位,x-1 x-2 … … 1,也就是 x - 1 x - 2 1
对于当前位的数值,有这么几种情况
- 当前位的数值为 0 ,也就是 x = 0
-
当前位的数值为1,也就是 x = 1
-
当前位的数值大于1,也就是 x > 1
int countDigitOne(int n) {
int ans = 0;
long i = 1; //i表示哪一位的数字,i = 1表示个位,i = 10 表示十位
while(n / i != 0)
{
//当前位的高位数字
long high = n / (10 * i);
//当前位的数字
long cur = (n / i) % 10;
//当前位的低位数字
long low = n - (n / i) * i;
if(cur == 0)
ans += high * i;
else if(cur == 1)
ans += high * i + low + 1;
else
ans += (high + 1) * i;
i *= 10;
}
return ans;
}
算法的时间复杂度为O(log n)
,也就是 n 有多少位,就循环多少次