编程之美解题笔记

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

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值