方法1:将从1到N的所有数枚举出来,每个数都计算一下出现了多少个1,最后相加就可以得到结果,复杂度是O(n*lnn)
方法2:通过每一个位上1出现的次数来计算总的出现次数,举例来说512,
各位出现1的数包括:
1 (1)
11, 21,31,..91 (9)
101,121,131,...191(10)
201,211,221...291(10)
301,311,321,...391(10)
401,411,421,...491(10)
501,511, (2)
各位贡献的是52个,(51+1)*1
十位贡献的1的个数为:
10, 11 ,12,....19(10)
110,111,...119 (10)
210, 211...219(10)
310,311,...319(10)
410,411,...419(10)
510, 511,512
(5)*10 + 2 +1
百位贡献的1的个数为:
100,,199 (100)
(0+1)*100
经过不断的这么分析,可以发现一个规律:
如果p位为0,那么该位贡献的结果为高位的数*10^p;
如果p为为1,那么该位贡献的结果为高位的数字*10^p + 低位数字 +1
如果p位为大于1,那么该位贡献的结果为(高位数字+1)*10^p
这个算法只和输入数字的长度有关系,输入数字的的长度为lnN,因此复杂度为lnN
依据上面的分析编写程序如下:
#include <stdio.h>
int Count1(int value) {
int num = 0;
while (value) {
if (value % 10 ==1) {
num++;
}
value = value / 10;
}
return num;
}
int Power(int radix, int exp) {
int result = 1;
for (int i = 0; i < exp; ++i) {
result *= radix;
}
return result;
}
int Count1_1(int value) {
int current_number = 0;
int higher_parts = 0;
int lower_parts = 0;
int radix = 1;
int num = 0;
while (value / radix) {
current_number = (value / radix) % 10;
lower_parts = value - (value / radix) * radix;
higher_parts = value / (radix * 10);
switch (current_number) {
case 0:
num += higher_parts * radix ;
break;
case 1:
num += higher_parts * radix + lower_parts + 1;
break;
default:
num += (higher_parts + 1) * radix ;
}
radix *= 10;
}
return num;
}
int main(int argc, char** argv) {
const int kValue = 512;
int num = 0;
for (int i = 0; i <= kValue; ++i) {
num += Count1(i);
}
printf("%d\n", num);
printf("%d\n", Count1_1(kValue));
}
参考文献:
http://www.nowamagic.net/algorithm/algorithm_CountOccurrencesOfOne.php
编程之美 2.4