一、需求
- 输入一个整数
n
,求1~n这n个整数的十进制表示中1出现的次数。 - 例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
示例 1:
输入:n = 12
输出:5
示例 2:
输入:n = 13
输出:6
二、规律法1
2.1 思路分析
- 题目要求1~n中1出现的次数,考虑将n转换为字符串,然后统计字符1的个数,但这样会超时;
- 既然1不行,换一种思路,将n转换为字符串后,可以容易确定n的位数;
- 利用for循环,从个位开始直到最高位,计算每一位上1出现的次数并做累加,关键是如何计算每一位上1出现的次数;
- 假设现在n = 345[i]xx,现在i到了第三位,假设要计算该位上1出现的次数,当 i 左边的高位部分取不到345时,即只取000~344, i 右边的低位部分可任意取即有00~99,共100种,从而这种情况下必有345*100个1;
- 上面描述的高位部分取不到345的情况,假设现在取到了,那么又要分两种情况:①2 <= i < =9,这种情况下 i 右边的低位 部分随便取,共100种;② i == 1时,这种情况下 i 右边的低位部分不能随便取了,只能取xx + 1种情况;
- 把所有情况加起来就是结果。
2.2 代码实现
class Solution {
public int countDigitOne(int n) {
int count = 0;
String str = ""+n;
char[] chs = str.toCharArray();
for(int i = 1; i <= chs.length; i++) {
//n取不到high(假设n=345[i]xx,high指345),该部分必然成立
int high = n/(int)Math.pow(10,i);
count += high*(int)Math.pow(10,i-1);
//n取到high...,根据第i位上的数分两种情况
int digit = chs[chs.length-i] - '0';
if(digit > 1) {
count += (int)Math.pow(10,i-1);
} else if(digit == 1) {
count += n % (int)Math.pow(10,i-1) +1;
}
}
return count;
}
}
2.3 复杂度分析
- 时间复杂度为O(N),其中N为元素n的位数;
- 空间复杂度为O(N),初始化字符数组申请了n个空间;
三、规律法2
3.1 思路分析
- 上面的空间复杂度都很高,考虑将能不能不申请空间,当然是可以的;
- 通过设计一个方法求一个整数的第 i 位数来代替字符数组的遍历问题;
3.2 代码实现
class Solution {
public int countDigitOne(int n) {
int count = 0;
String str = ""+n;
for(int i = 1; i <= str.length(); i++) {
//n取不到high(假设n=345[i]xx,high指345),该部分必然成立
int high = n/(int)Math.pow(10,i);
count += high*(int)Math.pow(10,i-1);
//n取到high...,根据第i位上的数分两种情况
int digit = getDigit(n,i);
if(digit > 1) {
count += (int)Math.pow(10,i-1);
} else if(digit == 1) {
count += n % (int)Math.pow(10,i-1) +1;
}
}
return count;
}
//获取一个整数的第i位数,第1位是个位,第2位是十位,以此类推
int getDigit(int n,int i) {
int res = 0;;
while(i != 0) {
res = n % 10;
n = n / 10;
i--;
}
return res;
}
}
3.3 复杂度分析
- 时间复杂度为O(N^2),计算次数由N变为1+2+3+...N了,时间复杂度提高了;
- 空间复杂度降低为O(1);