1的数目

来源:互联网 发布:护卫神php套件 编辑:程序博客网 时间:2024/04/29 23:41

题目描述

给定一个十进制正整数N,求出从1到N的所有整数中包含1的个数。比如给定N=23,则包含1的个数为13。其中个位出现1的数字有1,11,21,共3个,十位出现1的数字有10,11...19共10个,所以总共包含1的个数为3+10 = 13个。

 

自然的解法

最自然的想法莫过于直接遍历1到N,求出每个数中包含的1的个数,然后将这些个数相加就是总的1的个数。需要遍历N个数,每次计算1的个数需要O(lgN)(以10为底),该算法复杂度为O(NlgN)。当数字N很大的时候,该算法会耗费很长的时间,应该还有更好的方法。

 

更好的解法

一个更好的解法建立在对该题目进行深入分析的基础上。一种特殊的情况是可以直接得出求解的公式的,而针对常规的情况则需要分情况考虑。


特殊的情况(N=10^K-1)

如果N为K位数,且N为K位数的最大值,则1~N中1的个数为K*10^(K-1)。假定N为3位数,且是3位数中最大的数999,则我们考虑从001-999。先让个位为1,则剩下的两个位置有10*10=10^2种可能,都可能为0-9中任何一个。同理,十位或百位为1,则剩下两个位置也有10^2种可能的取值,所以总的取值可能为3*10^2=300个1。注意一点,如果是求1~999中包含1的数字的个数,那就不同了。如11就只能算一个数字,而在前面的分析中是要算两次的。改成这样也很容易求解,考虑1~999共999个数字,其中不包含1的数字有9^3-1 = 728个,所以包含1的数字个数为999-728= 271个。


常规情况(N为任意正整数)

我们可以从1位数开始分析,慢慢找寻规律。

当N为1位数时,对于N>=1,1的个数F(N)为1。

当N为2位数时,则个位上1的个数不仅与个位数有关,还和十位数字有关。当N=23时,个位上1的个数有1、11、21共3个,十位上1的个数为10,11...19共10个,所以1的个数F(N) = 3+10 = 13。如果N的个位数大于等于1,则个位出现1的次数为十位数的数字加1;如果N得个位数为0,则个位出现1的次数等于十位数的数字。

十位数上出现1的次数类似,如果N的十位数字等于1,则十位数上出现1的次数为各位数字加1;如果N的十位数字大于1,则十位数上出现1的次数为10。

当N为3位数时,同样分析可得1的个数。如N=123,可得1出现次数=13+20+24 = 57。

当N为4,5...K位数时,我们假设N=abcde,则要计算百位上出现1的数目,则它受到三个因素影响:百位上的数字,百位以下的数字,百位以上的数字。

如果百位上数字为0,则百位上出现1的次数为更高位数字决定。如N=12013,则百位出现1的数字有100~199, 1000~1199, 2100~2199...11100~111999共1200个,等于百位的更高位数字(12)*当前位数(100)。

如果百位上数字为1,则百位上出现1的次数不仅受更高位影响,还受低位影响。如12113,则百位出现1的情况共有1200+114=1314个,也就是高位影响的12*100+低位影响的113+1=114个。

如果百位上数字为其他数字,则百位上出现1的次数仅由更高位决定。如12213,则百位出现1的情况为(12+1)*100=1300。

有以上分析思路,写出下面的代码。其中low表示低位数字,curr表示当前考虑位的数字,high表示高位数字。

一个简单的分析,考虑数字123,则首先考虑个位,则curr为3,低位为0,高位为12;然后考虑十位,此时curr为2,低位为3,高位为1。其他的数字可以以此类推。

 

typedef unsigned long long ULONG;ULONG numOfone(ULONG n){    ULONG factor = 1, cnt = 0;  //factor为乘数因子    ULONG low=0, curr=0, high=0;    while (n / factor != 0) {        low = n - n/factor * factor;  //low为低位数字,curr为当前考虑位的数字,high为高位数字        curr = n/factor % 10;        high = n/(factor * 10);        switch(curr) {            case 0:   //当前位为0的情况                cnt += high * factor;                break;            case 1:   //当前位为1的情况                cnt += high * factor + low + 1;                break;            default:  //当前位为其他数字的情况                cnt += (high+1) * factor;                break;        }        factor *= 10;    }    return cnt;}


 

递归解法

何海涛先生的博客中还有一个递归的解法,可以参见http://zhedahht.blog.163.com/blog/static/25411174200732494452636/

 

 

原创粉丝点击