[leetCode]剑指 Offer 43. 1~n整数中1出现的次数
递归
直观思路是遍历每一个数字,对每一个数字采用“%”“/”的方法取个位判断是否为1然后累加。由于数字n有O(logn)为所以时间复杂度为O(nlogn)。应该考虑更快的算法。
假设输入的数字n为21345,可以把它分为两部分:
- 1~1345
- 1346~21345
先来分析1345~21345这部分。从万位开始万位出现1的次数为10000,因为可取10000 ~ 19999,1出现了10000次。剩余4位1出现的次数又是多少呢?把1345 ~ 21345 再分为两部分:
- 1346 ~ 11345
- 11346 ~ 21345
由排列组合可知这两部分剩下4为每一位都可以取1,取1后剩下的3位有0~9种取法所以一共有 2 ∗ C 4 1 ∗ 1 0 3 = 8000 2 * C_4^1 * 10^3 =8000 2∗C41∗103=8000种。
最后剩下的1~1345 可以递归求解。由于递归的次数为n的位数所以时间复杂度位O(logn)
class Solution {
public int countDigitOne(int n) {
if(n <= 0) return 0;
// 为方便递归计算将数字转换为字符串
String strN = String.valueOf(n);
return countDigitOne(strN);
}
private int countDigitOne(String strN) {
int len = strN.length();
if(strN == null || len == 0)
return 0;
// 第一位数字
int first = strN.charAt(0) - '0';
// strN只有1位,且为0
if(len == 1 && first == 0)
return 0;
// strN只有一位1只能出现1次
if(len == 1 && first > 0)
return 1;
// 假设strN是21345
// numOfFirstDigit是第一位1出现的数目:10000~19999;
int numOfFirstDigit = 0;
if(first > 1)
numOfFirstDigit = powerBase10(len - 1);
else if(first == 1)
// 最后要+1 因为first本身为1
numOfFirstDigit = Integer.valueOf(strN.substring(1,len)) + 1;
// numOfOtherDigits是1346~21345中除第一位外的数位中的数目
int numOfOtherDigits = first * (len - 1) * powerBase10(len - 2);
// 1~1345中的数目
int numRecursive = countDigitOne(strN.substring(1,len));
return numOfFirstDigit + numOfOtherDigits + numRecursive;
}
// 求10的n次幂
private int powerBase10(int n) {
int result = 1;
for(int i = 0; i < n; ++i) {
result *= 10;
}
return result;
}
}