[读书笔记]编程之美(二)
2.1求二进制数中1的个数
- 问题:对于一个字节(8bit)的无符号整形变量,求其二进制表示中“1”的个数,要求算法的执行效率尽可能高。
- 思路:
n = n & (n-1);
当然因为只有8bit所以直接定义一个256的int数组表,直接返回结果,时间复杂度是O(1)。
2.2不要被阶乘吓到
- 问题:1.给定一个整数N,那么N的阶乘N!末尾有多少个0呢?例如:N=10,N!=3628800,N!的末尾有两个0。
2.求N!的二进制表示中最低位1的位置。 - 思路:从“那些数相乘能得到10”这个角度来考虑,对N!进行质因数分解,N!=(2X方)(3Y)(5Z)…所以res = min(X,Z)。
问题1的解法一
for(int i = 1; i <= N; i++)
{
j = i;
while(j % 5 == 0)
{
res ++;
j /= 5;
}
}
解法二: Z = [N/5] + [N/25] + [N/125] + …
while(N)
{
res += N/5;
N /= 5;
}
问题二:等于求N!含有质因数2的个数。即等于N!含有质因数2的个数加一。
while(N)
{
N = N >>= 1;
res += N;
}
N!含有质因数2的个数,还等于N减去N的二进制中表示1的数目。
2.3寻找发帖“水王”
- 问题:传说,Tango有一“水王”,他不但喜欢发帖,还会回复其他ID发的每个帖子。坊间风闻该“水王”发帖数目超过了帖子总数的一半。如果你有一所有帖子(包括回帖)的列表,其中帖子作者的ID也在表中,你能快速找到这个传说中的Tango水王吗?
- 思路:如果每次删除两个不同的ID(不管是否包含”水王“的ID),那么,在剩下的ID列表中,”水王“ID出现的次数仍然超过总数的一半。
for(int i = nTimes = 0; i < N; i++)
{
if(nTimes == 0)
{
candidate = ID[i], nTimes = 1;
}
else
{
if(candidate == ID[i])
nTimes++;
else
nTimes--;
}
}
return candidate;
2.4 1的数目
- 问题:1.给定一个十进制正整数N,写下从1开始,到N的所有整数,然后数一下其中出现所有”1“的个数。f(12) = 5。
2.满足条件“f(N) = N”的最大的N是多少? - 思路:假设N=abcde,这里a、b、c、d、e分别是十进制数N的各个数位上的数字。如果要计算百位上出现1的次数,它将会受到三个因素的影响:百位上的数字,百位以下的数字,百位以上的数字。
while(n / iFactor != 0) //这里iFactor可以是0~9任意一个数
{
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;
}
问题二的解法:
容易从上面的式子归纳出: f(10n−1)=n∗10n−1 。容易得出 n=1010−1 时, f(n) 的值大于n。
通过类似数学归纳法的思路来推理这个问题,证明满足条件 f(n)=n 的数存在一个上界。
计算这个最大数n
令 N=1011−1=99999999999 ,让n从N往0递减,依次检查是否有 f(n)=n ,第一个满足条件的就是我们要求的整数。得出n = 1 111 111 110 是满足 f(n)=n 的最大整数。
2.5寻找最大的K个数
- 问题:有很多无序的数,假定他们各不相等,怎么选出其中最大的若干个数呢?
- 思路:
1.在数据量不大的情况下,可以选择快排或者堆排序O( N∗/log2N ),选择排序和交换排序O( N∗K )。在 K(