【有趣的面试算法题】之二 求1~n的整数中1的个数,递归统计区间块中1的数量

来源:互联网 发布:mac os sierra安装教程 编辑:程序博客网 时间:2024/05/29 05:54

题目:求1~n的整数中1的个数。

常规做法是,先写一个右移判1的子函数,计算每个数里面的1的个数,然后遍历1~n,取和,就是答案。


在百度文库(http://wenku.baidu.com/view/6722a969af1ffc4ffe47ac18.html)中看到有这样一个解法:


//计算整数 1~n 中的1的个数总和ULONGLONG Sum1s(ULONGLONG n) {       ULONGLONG iCount = 0;    ULONGLONG iFactor = 1;     ULONGLONG iLowerNum = 0;    ULONGLONG iCurrNum = 0;    ULONGLONG iHigherNum = 0;     while(n / iFactor != 0)     {         iLowerNum = n - (n / iFactor) * iFactor;        iCurrNum = (n / iFactor) % 10;         iHigherNum = n / (iFactor * 10);        switch(iCurrNum)        {        case 0:              iCount += iHigherNum * iFactor;            break;         case 1:                iCount += iHigherNum * iFactor + iLowerNum + 1;             break;         default:               iCount += (iHigherNum + 1) * iFactor;            break;         }          iFactor *= 10;     }      return iCount;} 

据它说“ 这个方法只要分析N就可以得到f(N),避开了从1到N的遍历,输入长度为Len的数字N的时间复杂度为O(Len),即为O(ln(n)/ln(10)+1)。在笔者的计算机上,计算N=100 000 000,相对于第一种方法的40秒时间,这种算法不到1毫秒就可以返回结果,速度至少提高了40 000倍。”



但不知为何没能运行正确,尽管没有看懂思路,但觉得还蛮有启发,直接从 n 入手分析,避免遍历1~n,于是我研究一会之后,观察到有这么一个现象:在二进制表示中,N位的000...000 ~111...111 之间0 与 1的个数相等!如下图所示:

                                       


于是基于递归法的实现方法如下所示,并且递归深度不会超过 n 的最高非0 bit位数,比较快吧?

//计算整数 1~n 中的1的个数总和ULONGLONG Sum1s(ULONGLONG n) {       if (n <= 2)    {        return n;    }        ULONGLONG iFactor = 3;     ULONGLONG iLen = 1;    while(n >= iFactor) //找出最大的、小于n的、bit位全部为1 的数    {         iFactor <<=1;        iFactor |= 1;        ++iLen;    }      iFactor >>=1;        ULONGLONG iCount =((iFactor + 1)*iLen)>>1; //这一片区域的1 0 数量相等    if ( iFactor == n)    {        return iCount;    }    else    {        return (iCount + n - iFactor + Sum1s(n & (iFactor)));    }    }