2.1 求二进制数中1的个数
解法一:除2的余数
解法二:移位
解法三:v&(v-1)
解法四:查表法(空间换时间)
2.2 不要被阶乘吓倒
求N!中含质因数K的个数Z=[N/K]+[N/K^2]+[N/K^3]+…
ret = 0
while(N)
{
ret += N / K;
n /= K
}
求N!中的末尾0的个数,即求N!中含质因数5的个数
求N!的二进制表示中最低位1的位置,即求N!中含质因数2的个数+1
2.3 寻找发帖水王
基本思路:
方法一:排序,取中位数
方法二:每次删除两个不同的ID,水王ID出现的次数仍然占总数的一半,O(n)
2.4 1的数目
题目:给定一个十进制正整数N,写下从1开始,到N的所有整数,然后数一下其中出现的所有“1”的个数
方法一:遍历每个数,找到每个数中1的个数
while(n != 0)
{
iNum += (n%10==1)?1:0;
n /= 10;
}
方法二:1的个数=个位上出现1的个数+十位上出现1的个数+百位上出现1的个数+…
对abcde,假设c为当前位,则当前位数为100,ab为高位,de为低位,计算c位上出现1的个数:
若c=0, 出现1的个数 = 高位数 * 当前位数
若c=1, 出现1的个数 = 高位数 * 当前位数 + 低位数 + 1
其他, 出现1的个数 = (高位数+1) * 当前位数
设当前位数为iFactor,则低位数 = N – (N/iFactor)*iFactor
高位数 = N / (iFactor*10)
当前位数 = (N / iFactor ) %10
2.5 寻找最大的K个数
方法一:排序(K较大时,快排O(nlgn),K较小时,选择排序或冒泡排序O(n*k))
方法二:快排的演变
方法三:二分搜索。设所有整数由m bit表示,按照整数第m-1 bit上的数为0或1,将整数分为两组,[0,2^(m-1)-1]和[2^(m-1),2^m-1],考察区间的整数个数,再接着从相应的区间中,考虑按第m-2 bit的数来区分,依次类推。
方法四:堆排序,最小化堆O(nlgk)
方法五:改变计数排序O(n),要假设N个数都是正整数,且他们的取值范围不太大。
注意:当数字很大,K很大,内存有限时,可以考虑修改方法二,将数字存入文件中,每次遍历文件。也可以考虑修改方法五,将区间分为M块,然后扫描所有元素,统计各区间内的数字的个数,然后再对小区间进行分块处理。
2.6 精确表达浮点数
对X=0.a1a2a3…an (b1b2b3…bm)
有10^n*X=a1a2…an + 0.(b1b2…bm)
令Y=0.(b1b2…bm)
则10^m*Y = b1b2…bm+0.(b1b2…bm)=0.b1b2…bm+Y
由此可以计算出Y,从而算出X的分数表示,注意还要通过求最大公约数来规约。
2.7 最大公约数问题
方法一:gcd (x,y)=gcd(y, x%y),缺点:要计算x%y,费时
方法二:gcd (x,y)=gcd(x,x-y),缺点:迭代次数增多,当y很小, x很大时,费时
方法三:若x, y都有某个素因子k,则gcd(x,y)=k*(gcd (x/k,y/k))
若x有素因子k, 而y没有,则gcd(x,y)=gcd(x/k,y)
可以取k为2,则若x, y都是偶数,则gcd(x,y)=gcd(x>>1, y>>1) << 1
若x,y其中有一个是偶数(假设y),则gcd(x,y)=gcd(x, y>>1)
若x,y都是奇数,则gcd(x,y)=gcd(x,x-y),假设x-y>0,接下来,x-y一定是偶数。
2.8 找符合条件的整数
题目:给定一个正整数N,求一个最小的正整数M (M>1),使得N*M的十进制表示形式里只含有1和0。
2.9 斐波那契数列
方法一:递归
方法二:空间换时间,用O(n)的数组保存之前已经计算过的值,时间复杂度O(n),空间复杂度O(n)
方法三:求解通项公式,直接计算,O(1)
方法四:分治策略。(Fn Fn-1)=(Fn-1 Fn-2)*A, 其中A是一个2*2的矩阵。接下来只需计算(Fn Fn-1)=(F1 F0)*A^n-1,问题转化为求A^n-1。
求A^n的方法,将n用二进制表示,
for(;n;n>>1)
{
if(n & 1)
result *= A;
A *= A;
}
2.10 寻找数组中的最大值和最小值
算法导论中也讲过了,还是蛮简单的,设两个变量min, max,比较1.5*N次即可。
这里提到了一个分治法,比较次数虽然没有减少,但算法思路值得借鉴。
求前后N/2个数的min和max,然后取较小的min,较大的max即可。