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