编程之美解题笔记

来源:互联网 发布:usb编程宝典 编辑:程序博客网 时间:2024/05/18 02:53

这里的解法均采用书中最优解法,是否还有其它更好的解法,读者可以自己去尝试。如果有,请留言分享一下,感激不尽!

1、问题描述:假设有一个没有头指针的单链表。一个指针指向此单链表中间的一个节点(不是第一个,也不是最后一个节点),请将该节点从单链表中删除。

分析与解法:记下后续结点的值;把当前结点的下一个节点的下一个节点连接到当前节点的next(pCurrent.next=pCurrent.next.next);把后续结点值赋给当前结点的值。

2、给出两个单向链表的头指针,判断这两个链表是否相交(假设两个链表均不带坏)

分析与解法:1)把第二个链表接在第一个链表的末尾,从第二个链表开始遍历,看是否会回到起始点(根据起始点的唯一内存地址判断)。2)如果两链表相交,则两链表的最后一个节点的内存地址一定相同,分别遍历两链表,判断最后一个节点的内存地址是否相同。

3、求二进制数中1的个数

分析与解法:假设目标二进制数为B,num为1的个数。1)每次与0x01即B&0x01:while(B){num+=B & 0x01;B>>=1;}。2)每次&(自身-1)即B&(B-1):while(B){B &=(B-1); num++;} return num。

4、求整数N的阶乘N!末尾有多少个0

分析与解法:假设N!=2(x)*5(z)*......括号中表示多少次方2(x)表示2的x次方。0的个数M=min{x,z}。不难看出x大于z(读者自己慢慢研究,最好是从1到10一个一个的想)。所以M=z。

1)直接求5的个数

int ret=0;for(int i=1;i<=N;i++){int j=i;while(j%5==0){ret++;j/=5;}}

2)公式:z=[N/5]+[N/5(2)]+[N/5(3)]+....,其中()中表示次方,如5(2)表示5的2次方。(这个我还没有想明白,哪位高手帮忙指点一下,谢谢!)

int ret=0;while(N){ret+=N/5;N/5;}
5、精确表达浮点数在计算机中,使用float或者double来存储小数是不能得到精确值的。如果你希望得到精确计算结果,最好是用分数形式来表示小数。有限小数或者无限循环小数都可以转化为分数。解法见:http://blog.csdn.net/love254443233/article/details/9114097

6、最大公约数问题。

分析与解法:

1)辗转相除

 int gcd(int x , int y) {    return (y == 0 )?x :gcd(y , x % y) ;      }
2)辗转相减法

 int gcd(int x , int y) {    if(x < y)     return  gcd(y , x) ;      else if(y == 0)     return x ;    else     return gcd(x - y , y) ;  } 
3)第一种方法递归次数相当少但每次取模挺耗时,而第二种递归可能会相当多,但每次运算都是减法运算,但是运算次数很多。最后提出一种相除和相减相结合的方法,保证了不做过多冗余的步骤

若x,y均为偶数,f(x,y) = 2*f(x/2,y/2) = 2*f(x>>1,y>>1)

若x为偶数,y为奇数,f(x,y) = f(x/2,y) = f(x>>1,y)

若x为奇数,y为偶数,f(x,y) = f(x,y/2) = f(x,y>>1)

若x,y均为奇数,f(x,y) =f(y,x-y)

 int gcd(int x , int y) {   if(x < y)      return gcd(y , x) ;   if(y == 0)      return x ;    if(isEven(x))    {      if(isEven(y))             return gcd(x - y, y) ;               else         return gcd(x , y >>1) ;                }        else    {       if(isEven(y)) //y为奇数                     return gcd(x >> 1, y) ;               else         return 2 * gcd(x >> 1, y >> 1) ;    } }
6、快速寻找满足条件的两个数:输入一个数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字。
要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。例如输入数组1、2、4、7、11、15和数字15。由于4+11=15,因此输出4和11。

方法:先排序(n*logn),然后用两个指针i,j,各自指向数组的首尾两端,令i=0,j=n-1,然后i++,j--,逐次判断a[i]+a[j]?=sum,如果某一刻a[i]+a[j]>sum,则要想办法让sum的值减小,所以此刻i不动,j--,如果某一刻a[i]+a[j]<sum,则要想办法让sum的值增大,所以此刻i++,j不动。所以,数组无序的时候,时间复杂度最终为O(n*logn+n)=O(n*logn),若原数组是有序的,则不需要事先的排序,直接O(n)搞定,且空间复杂度还是O(1)。

while (i<j)     {         int plus = A[i]+A[j];         if (plus == sum)         {             printf("(%d,%d) ",A[i],A[j]);             i++, j--;         }         else if (plus < sum)             i++;         else             j--;     } 

--------会一直续

转载请注明本文出处:http://blog.csdn.net/love254443233

原创粉丝点击