问题:
给定一个十进制正整数N,写下从1开始,到N的所有整数,然后数一下其中出现的所有“1”的个数。
例如:
N= 2,写下1,2。这样只出现了1个“1”。
N= 12,我们会写下1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12。这样,1的个数是5。
直观思路是,逐个计算出1 ~ N 这 N 个数含有 1 的个数。
如 1 ~ 12345 :判断最后 1 位是否为 1,然后每一次右移一位,重复上述步骤。
判断 1 个数的复杂度
改进思路:能否直接计算出给定 1 ~ N 的 1 出现次数:
如 12,个位出现 1 的次数为2,十位出现 1 的次数是 3,则总次数为 5
如 23,个位出现 1 的次数为 3 (1,11,21),十位出现 1 的次数为 10 (11,12,…,19),则总次数为 13
即只要求出每一位可能出现 1 的次数,则能够求出 1 ~ n 出现 1 的次数。
我们通过逐位判断的方式,以百位数字为例:
如果百位数字是 0 则百位出现 1 的次数,只由更高位决定:
如 12045:百位是 1 的次数
100~199
1100~1199
2100~2199
。。。
11,100~11,199
每一行含有 100 个,共 12 行;故百位出现 1 的次数 更高位数字(12) * 当前位数(100)
如果百位数字是 1 则百位出现 1 的次数,不仅由更高位决定,还和当前位的低位有关
如 12145:百位是 1 的次数,除了上述高位决定的 12*100 次
低位部分 12,100 ~ 12,145 百位出现 1 的次数等于低位数字(45) + 1
如果百位数字大于 1 (2~9),则百位出现 1 的次数,只由更高位决定
12345 : 百位是 1 的次数
100~199
1100~1199
2100~2199
。。。
11,100~11,199
12,100~12,199
每一行含有 100 个,共 13 行;故百位出现 1 的次数 更高位数字加一(12+1) * 当前位数(100)
上述算法的时间复杂度:
#include <iostream>
using namespace std;
int Counts1OfN(int n) {
int counts = 0;
int base = 1;
int low = 0; //低位数字
int cur = 0; //当前位
int high = 0; //高位数字
while (n/base > 0) {
//如12345:base = 100, cur = 3, low = 45, hight 12
low = n % base;
cur = (n / base) % 10;
high = n / (base*10);
switch(cur) {
case 0:
counts += high*base;
break;
case 1:
counts += high*base + (low+1);
break;
default:
counts += (high+1)*base;
}
base *= 10;
}
return counts;
}
int main()
{
int nums[] = {4,13,33,103,113,123,9999,33342124};
for (int i = 0; i < sizeof(nums)/sizeof(nums[0]); i++) {
cout << "0 ~ " << nums[i] << " 包含 1 的个数:" << Counts1OfN(nums[i]) << endl;
}
}
结果:
1 ~ 4 包含 1 的个数:1
1 ~ 13 包含 1 的个数:6
1 ~ 23 包含 1 的个数:13
1 ~ 33 包含 1 的个数:14
1 ~ 103 包含 1 的个数:25
1 ~ 113 包含 1 的个数:40
1 ~ 123 包含 1 的个数:57
1 ~ 9999 包含 1 的个数:4000
1 ~ 33342124 包含 1 的个数:34077658
参考:《编程之美》
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文:http://blog.csdn.net/quzhongxin/article/details/47362993