剑指 Offer 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
注意:本题与主站 233 题相同:https://leetcode-cn.com/problems/number-of-digit-one/
解法一:暴力求解
最直观的做法,就是累加1 ~ n中每个整数出现1的次数,我们可以通过对10求余数判断整数的个位数字是不是1,如果这个数字大于10,则除以10之后再判断个位数字是不是1。
Java代码
class Solution {
public int countDigitOne(int n) {
int res = 0;
for(int i = 1;i <= n;i++){
int num = i;
while(num != 0){
if(num % 10 == 1) res++;
num /= 10;
}
}
return res;
}
}

遗憾的是TLE了,在上述思路中,我们对每个数字都要做除法和求余运算,以求出该数字中1出现的次数。而我们知道,对于一个数字n,它有
O
(
l
o
g
n
)
O(logn)
O(logn)位,如100有
l
o
g
10
100
+
1
=
3
log_{10}100 +1=3
log10100+1=3 位,即求一个数n有多少位的时间复杂度是
O
(
l
o
g
n
)
O(logn)
O(logn)的,又我们需要求n个数字,即n次,那么总时间复杂度就是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),当输入的n非常大的时候,需要大量的计算,运算效率不高,导致超时了。
解法二:数位统计
我们假设 n = abcdef,其中等号右边a为1~9中的某个数字,其余的可为0~9中某个数字,我们需要做的就是统计每个位上,如个位、十位、百位…上能出现1的次数的总和。
假设求c位置为1的个数:
此时c左边和右边的值分别为 lVal = ab, rVal = def , 而t = 1000,表示c的位置是千位的位置
此时则有如下两种情况:
lval取[0, ab-1];右边可以从0取到999,也就是此时c位置可以出现1000次1,我们用t表示,因为t就是1000, 此时res += lval * t;lval取ab;- 如果
c=0,没有任何元素,及c位此时一次1都无法出现 - 如果
c=1,右边可以取[0,def]也就是def+1个元素,res += def+1 - 如果
c>1,右边可以从0取到999,也就是t=1000, 此时res += t;
- 如果
举例 :
假如n = 123456
- 假设当前计算千位上出现
1的个数,在千位上有_ _x_ _ _,x的左边有12,右边有456
当左边是[0,11]时,千位上可以为1,且千位右边的三位可以是0~999,即千位上的数字我们固定为1,左边是[0-11]的任意一个数字时,右边可以是0~999,共1000个选择,所以千位是1的个数是12 * 1000 - 如果左边是
12了x = 0;千位上没有1x = 1,那么前三位固定了是121,但后三位可以是0~456,共457个数,故千位出现1的次数应加上457x > 1;此时我们如果把千位x固定为1,后面三位可以是0~999,共1000个数,故千位出现1的次数应加上1000,n = 123456时就是走的该情况。
Java代码
class Solution {
public int countDigitOne(int n) {
int res = 0;
List<Integer> numbers = new ArrayList<>();
while (n != 0){
numbers.add(n % 10);
n /= 10;
}
for (int i = numbers.size() - 1; i >= 0 ; i--) {//枚举每一个数位、个位、十位、百位...
int lVal = 0, rVal = 0; // i位置元素 左右两边的值
int t = 1;
//数位左边的数字
for (int j = numbers.size() - 1; j > i; j--) lVal = lVal * 10 + numbers.get(j);
//数位右边的数字
for(int j = i - 1; j >=0; j--) {
rVal = rVal * 10 + numbers.get(j);
t *= 10;
}
// 左边
res += lVal * t;
// 右边
if (numbers.get(i) == 1) {
res += rVal + 1;
} else if (numbers.get(i) > 1) {
res += t;
}
}
return res;
}
}

1~n整数中1出现次数算法
本文介绍了一种高效算法,用于计算从1到n的所有整数中数字1出现的总次数。通过避免传统暴力求解的低效性,采用数位统计的方法,显著提高了计算速度。
2184





