编程之美学习心得 二 (未完待续)

2.1 求二进制数中1的个数      解法一:暴力的解法           对N中二进制1的个数:N = b[0] +b[1]*2+b[2]*2^2+…b[n]*2^n                      int count(BYTE v){                int num = 0 ;                while(v)                {                     if(v%2==1){                          num++;                     }                     v>>1;                }                return num;           }      解法二:位操作           如果该二进制最后位上为1,与0x01相与,结果为1;然后将二进制右移           int count(BYTE v)           {                int num = 0;                while(v)                {                     num += v&0x01 ;                     v>>1;                }                return num;           }      解法三:v &(v-1)           int count(BYTE v){                int num = 0;                while(v){                     v = v&(v-1);                     sum ++;                }                return sum;           }      解法四:使用分支 (利用空间换取时间)               利用switch case语句,将每个数字的1的个数设置好,直接返回。                实际执行效率可能会低于方法二和三。具体要看分支执行情况。      方法五:查表法                提前将每个数1的个数放到一个表中,直接读取这个数组。

2.2 求N!中末尾0的个数  和二进制表示中最低位1的位置。      解法1:最直接的解法是找出能是末尾为0的那些数,通过分析N!=(2^x)*(3^y)*(5^z)…..                     由于只有2*5=10,才能是末尾为0;由于N! 中2的个数远多于5,所以找0的个数实际上是找N!分解5的个数。                int ret = 0;                for(i=1;i<=N;i++)                {                     j=i;                     while(j%5==0){                          ret++;                          j/=5;                     }                }

     解法2:Z= [N/5]+[N/5^2]+[N/5^3]+…   总会存在k使得[N/5^k]=0;                [N/5]所有小于等于N的数中5的倍数贡献一个5                [N/5^2]所有不大于N的书中5^2的倍数贡献一个5                                int ret=0;                while(N){                     ret += N/5;                     N/=5;                 25         …20…. 15  14  13  12  11  10  9  8  7  6  5  4  3  2  1  N/5:          1              1        1                         1                  1 N/5^2        1 25!中包含6个5因子。故有6个0;

求二进制中最低位1的位置,需要确定二进制末尾有多少个0;由于一个数*2,其二进制向左移移位,0的个数即N!中包含多少个2因子。 解法1:                result = [N/2]+[N/2^2]+[N/2^3]+…                       int lowestOne(int N){                int ret = 0 ;                while(N){                                          N>>1;                     ret += N;                }                return ret;           } 解法2: N!中因子2的个数=N-N中二进制1的数目。 假设N=11011; N!中2因子的个数count =  [N/2]+[N/2^2]+[N/2^3]+…                                   =  1101 +110+11+1                                  =  (1000+100+1)+(100+10)+(10+1)+1                                  =  (1111)+(111)+1                                  = (10000-1)+(1000-1)+(10-1)+(1-1)                                 = 11011 – N中1的个数

给定数N判断是否为2的方幂   n>0&&n&(n-1)==0

2.3 寻找发帖王水           找出那个ID号出现次数超过总数一半的ID。      通常做法是:先将所有ID排序,然后遍历ID统计ID出现的次数,找超过一般次数的ID。      对于已经有序的ID,则不需要对ID进行遍历,取N/2处的ID即是所找的ID。

     有没有一种不需要排序就能找出王水的ID的方法?      每次取两个不同的ID,那么剩下的ID中王水出现的次数仍超过一半。从而不断的重复这个过程,直到找到答案。      Type Find(Type *ID,int N)      {           Type candidate;           int nTimes,i;           for(i=0; i<N;i++){                if(nTimes==0){                     candidate = ID[i];                     nTimes =1;                }                esle{                     if(candidate == ID[i]){                          nTimes ++;                     }else{                          nTimes–;                     }                }           }           return candidate;       }

2.4 1的个数   写下1到N所有数中1的个数     设N= 12013,计算百位上1的个数的数字有百位数字、百位以上数字和百位一下数字决定。

     12013如果百位数字为0;则百位数字为1的情况有100-199、1100-1199、……、11100-11199一共有12*100=1200个;      12113如果百位数字为1:则百位数字为1的情况有100-199、1100-1199、……、11100-11199、12100-12113一共有12*100+13+1=1214;      12313如果百位数字为其他:则百位数字为1的情况有100-199、1100-1199、……、11100-11199、12100-12199一共有(12+1)*100=1300;

     所以需要计算当前位的值,低位的值,高位的值,      
LONGLONG 
Sum1s
( 
ULONGLONG 
n 
)
{
       
ULONGLONG 
iCount 
=0;
 
     
ULONGLONG 
iFactor 
=1;
       
ULONGLONG 
iLowerNum 
=0;
       
ULONGLONG 
iCurrentNum 
=0;
       
ULONGLONG 
iHighNum 
=0;


       
while
(
n 
/
iFactor 
!=0){
             
iLowerNum 
= 
n 

n
/ 
iFactor
*
iFactor 
;
             
iCurrentNum
=
n 
/
iFactor
%10;
             
iHighNum 
= 
n 
/(
iFactor
*10);


             
switch 
(
iCurrentNum 
)
            {
             
case 
0:
                   
iCount 
+= 
iHighNum 
*
iFactor
;
                   
break
;
             
case 
1:
                   
iCount 
+= 
iHighNum 
*
iFactor 
+ 
iLowerNum
+1;
                   
break
;
             
case 
2:
                   
iCount 
+= (
iHighNum 
+1)*
iFactor
;
                   
break
;
            }
             
iFactor 
*= 10;
      }
 
    
return
 
iCount  
;   
}


2.5 寻找最大k个数
解法1:  排序 取前k个数  复杂度O(N*logN)+o(k)
解法2:  快排中的第一步,将待排数分成两组,其中一组的任意一个数比另外一组任意数要大,在对两个分组做类似的操作。。。           假设有数组S,从S中中随机找到一个元素x,把数组分成两部分Sa和Sb。Sa中的元素大于X,Sb中的元素小于X。这时:      1. Sa中的元素个数小于K,Sa中所有元素和Sb中最大的K-|Sa|个元素      2. Sa中的元素个数大于等于K,则需要直接返回Sa中最大的K个数。

方法4:利用最大堆最小堆。湘江K个元素放在最小堆中,每次新加入一个元素X,如果X比堆顶元素小,则不考虑,反之,将该元素替换堆顶元素,更新最小堆。时间复杂度为O(nlogK) 方法5: 利用桶排序,对于N个数取值范围有限,统计每个数出现的次数用数组count(MaxN)记录,从最大的数的个数取出大于K个数为止。

2.6 精确的表示浮点数 对于有限小数或者无限循环小数都可以转化为分数来存储。 例如:0.9= 9/10           1.333(3) = 1/3

对于有限小数X= 0.A1A2A3…An=A1A2…An/10^n 对于无限循环小数  X=0.A1A2A3…An(B1B2B3..Bm)                    10^n*X=A1A2…An.(B1B2…Bm)                               =A1A2..An+0.B1B2…Bm(B1B2…Bm) = A1A2…An +Y     Y= 0.B1B2…Bm(B1B2…Bm) 
10^m*Y = B1B2…Bm+0.B1B2…Bm(B1B2…Bm)=B1B2…Bm+Y
Y=B1B2…Bm/(10^m-1)


X = (A1A2…An+B1B2..Bm/(10^m-1))/10^n
剩下的问题是找出分子和分母的最大公约数进行约分,求出最简分数。求最大公约数参考下一节。


2.7 最大公约数问题
f(x,y)=x,y的最大公约数,取k=x/y;b=x%y;则x=ky+b;(x>=Y)  如果一个数可以被x,y同时整除,则这个数必将可以被y,b整除, f(x,y)=f(y,b)=f(y,x%y);

对于大整数来说,模运算带来的开销比较大,由于x,y的约数一定是x-y,y的约数, f(x,y) = f(x-y,y)

BigInt gcd(Bigint X,BigInt Y){      if(x<y)           return gcd(y,x);      if(y==0)           return x;      else           return gcd (x-y,y) } 但对于大整数来说,减法带来运算次数的开销比较大,尤其是gcd(10000000,1)这种情况。

对于x,y来说,如果x= kx1,y=ky1;则f(x,y)=k*f(x1,y1); 如果x=px1,p是素数,y%p!=0;则f(x,y)= f(x1,y); 对算法的改进:      取p=2; 当x,y都是偶数,f(x,y)=2*f(x>>1,y>>1) 当x为偶数,y为奇数,f(x,y)=f(x>>1,y) 当x为奇数,y为偶数,f(x,y)=f(x,y>>1); 当x,y都是奇数,f(x,y)=f(x-y,y)

BigInt gcd(BigInt x,BigInt y){      if(x<y)           return gcd(y,x);      if(y==0)           return x;      else      {           if(IsEven(x)){                if(IsEven(y)){                     return gcd(x,y-x);                }else{                     return gcd(x,y>>1);                }           }else{                if(IsEven(y)){                     return gcd(x>>1,y);                }                 else{                     return 2*gcd(x>>1,y>>1);                }           }      }  }

2.9 Fibonacci数列

F(n) = 0   n=0        = 1     n=1        = F(n-1) + F(n-1)  n>1 一般解法:      int Fibonacci(int n){           if(n==0)                return 0;           else if(n==1){                return 1;           }else                return Fibonacci(n-1) + Fibonacci(n-2);      }   

在递归调用时通过数组记录已经计算过的值,这样以空间换取时间,时间复杂度为O(n)。

解法2: 求解通项公式           F(n)=F(n-1)+F(n-2) 特征方程为X^2=X +1 有根:X = (1+5^0.5)/2   X=(1-5^0.5)/2 F(n)=A*X1^n+B*X2^n 由F(0) = 0,F(1)=1;==>A=5^0.5/5   B=-A 由于引入了无理数,不能保证精度。

解法三:分治的方法      (F(n) F(n-1)) = (F(n-1)  F(n-2))*A

==> A=1   1             1   0 (Fn  Fn-1) = (Fn-1  Fn-2)A =…  = (F1  F0)A^(n-1)

A^(x*2) = (A^x)^2 用二进制表示n:      n=b0 +b1*2 +b2*2*2 +…+bk*2^k A^n = A^(b0 +b1*2 +b2*2*2 +…+bk*2^k)       = A^b0*(A^2)b1*(A^(2^2))^b2*…*(A^(2^k))^bk

所以求 A^(2^k) = (A^(2^(k-1)))^2

class Matrix; Matrix MatrixPow(Const Matrix &m,int n) {       Matirx result = Matrix::Identity;//单位阵      Matrix tmp= m;      for(;n;n>>1){           if(n&1){                result *= tmp;           }           tmp *=tmp;      } }

int Fabonacci(int n){      Matrix an = MatrixPow(A,n-1);      return F0*an(0,0) +F1*an(1,0); }
2.10 寻找数组中的最大值和最小值

解法1:穷举,找出数组中最大值和最小值,时间复杂度为2*N次,O(N); 解法2:将数组中的数按相邻两个放在一组,比较每组中较大的放在偶数位,较小的放在奇数位,需要N/2次比较           从偶数中比较选出最大的那个即为最大值,需要N/2次比较;           从奇数中比较选出较小的那个即为最小值,需要N/2次比较;总共需要1.5N次比较。 解法三:解法二破坏了原来数组的顺序,解法三是不破坏数组的顺序的。      利用两个变量max和min分别记录最大值和最小值,通过相邻的两个比较,去除较大的放在max中,取出较小的放在min中,然后再比较下两个数,将较小的与min比较,将较大的与max比较。

解法四:利用分治的思想,在前N/2和后N/2中分别找出一个min和max,然后去较小的min和较大max           f(N)=2f(N/2)+2                =2*(2*f(N/2^2)+2))+2                =2^2*f(N/2^2)+2^2+2                =2^(logN-1)*f(N/2^(logN-1))+2^logN+…+2^2+2                =N/2*f(2)+2*(1-2(logN-1))/(1-2)                =N/2+N-2                =1.5N-2

2.11 寻找最近点对

方法1 穷举  计算两两之间点的距离,比较它们求出最小值,时间复杂度为O(N*N);

方法2:对于数组有序,找最小的差值就容易了,快排的时间复杂度O(nlogn),找最小差值需要O(n);总的时间复杂度为O(nlogn)

方法3:分治的思想,去数组的中间数,将数字分成left和right两部分,分别找出left和right中的最小差值,然后和来自left的一个数和来自right的一个数的差值比较去最小值。时间复杂度O(nlogn)      对于平面二维的情况;将平面上的点映射到x轴上,去这些点的中点将这些点分成left和right两部分,并找出left中的最小值和right中最小值。取MDist=min(MinDist_right,MinDist_Right);取矩形区域MDist*2MDist,比较这个区域中的点中最小距离,因为在这个区域外的点在left和right间的距离肯定大于MDist,每次找两个区域间最短路径的时间消耗为O(N),二分法的时间复杂度为O(logN);总的时间复杂度为O(nlogn).

2.12 快速找满足条件的两个数

方法1:穷举法,计算两两数字之和是否满足条件 时间复杂度为O(n*n) 方法2:对于arr[i],判断另一个数字sum-arr[i]是否在数组中。      对于一个无序的数组查找一个数的复杂度为O(N),对于arr[i]中n个数的查找复杂度O(N*N)            另一个思路是先将数组排序,在利用二分查找复杂度为O(logN);快排的复杂度O(nlogN);总体时间复杂度依然为O(nlogn)

     然而,还有一种快速查找hash表,直接可以查找某个数是否存在,查找复杂度为O(1);这样对于N个数总体的复杂度为O(N); 解法三:      先排序,时间复杂度为O(nlogn);在排序的基础上,从前后遍历一次,令i=0;j=n-1;判断arr[i]+arr[j]时候=sum,若相等,则结束, 若小于sum,则i++;若大于sum,则j–;

     for(i=0,j=n-1; i<j;){           if(arr[i]+arr[j]==sum){                return (i,j);           }else if(arr[i]+arr[j]<sum){                i++;           }else{                j++;           }      } return (-1,-1);

    原文作者:王wwww
    原文地址: https://blog.csdn.net/qqqqqq999999/article/details/39085703
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞