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

题目:求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)));
    }
    
} 






猜你喜欢

转载自blog.csdn.net/zfdxx369/article/details/10101731