代码,非剑指offer 3

1.模拟实现strlen

int MyStrlen( const char* str ){    
   assert( NULL != str );   
     
   int len = 0;  
   while ('\0' != *str++)  
       len++;  
     
   return len;    
}
int MyStrlen( const char* src ){  
   assert( NULL != src );  
 
   if ('\0' == *src)  
       return 0;  
   else  
       return ( 1 + MyStrlen( ++str ) );  
}

2.

模拟实现memcpy、memmove和memset

strcpy和memcpy主要有以下3方面的区别。
1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容(内存中的内容),例如字符数组、整型、结构体、类等。
2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。

3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy

void* MyMemcpy( void* dest, const void* src, size_t n ){    
   assert( NULL != dest );    
   assert( NULL != src );  
 
   char* pDest = (char*)dest;    
   const char* pSrc = (const char*)src;  
   while (n--)  
       *pDest++ = *pSrc++;  
 
   return dest;    
}


void* MyMemmove( void* dest, const void* src, size_t count ){    
   assert( NULL != dest );    
   assert( NULL != src );   
 
   char* pDest = (char*)dest;    
   const char* pSrc = (const char*)src;    
   
   if (pDest <= pSrc || pSrc + count <= pDest)// 正常情况下,从前往后拷贝     
       while (count--)    
           *pDest++ = *pSrc++;    
   else    
       while (count--)    
           *(pDest + count - 1) = *(pSrc + count - 1);  
   
   return dest;    
}
void* MyMemset( void* dest, int c, size_t count )  {    
   assert( NULL != dest );  
 
   char* tmp = (char*)dest;  
   while (count--){    
       *tmp = (char)c;    
       tmp++;  
   }  
 
   return dest;    
}

memcpy
内存拷贝函数
source和destin所指的内存区域可能重叠,但是如果source和destin所指的内存区域重叠,那么这个函数并不能够确保source所在重叠区域在拷贝之前不被覆盖。而使用memmove可以用来处理重叠区域。函数返回指向destin的指针。


memmove
内存移动函数
如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。但复制后src内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。memmove函数能够反向拷贝,不用在意memcpy函数会遇到的问题。


memset
将dest中从当前位置开始的count个字节,用 c 替换并返回 dest 。

在strcpy,strncpy,memcpy和memmove这四个库函数中,安全性是递增的,前三个函数均没有考虑到内存重叠的问题,所以相对来说memmove函数的安全性最高。




3.  斐波那契数列: 

long long Fibonacci( unsigned n ){  
   int result[2] = {0, 1};  
   if (n < 2)  
       return result[n];  
 
   long long fibOne = 0;  
   long long fibTwo = 1;  
   long long fibN = 0;  
 
   // 注意这里是 i<=n, 而不是i<n  
   for (unsigned int i = 2; i <= n; ++i){  
       fibN = fibOne + fibTwo;  
 
       fibOne = fibTwo;  
       fibTwo = fibN;  
   }  
 
   return fibN;  
}



4.题目 :

1. 一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法?

 n = 1, 1 种方法 (一次 1 级台阶) 

 n = 2, 2 种方法 ( 两次 1 级,或者一次 2 级 ) 

前两种为基准情况 

 

 n = 3, 3( 三次 1 级,或一次 1 级一次 2 级 (顺序不同,两个方法) ) 

我们通过 台阶数为 3 来分析。青蛙一次只能跳一级或者两级 

也就是说: 青蛙跳上三级台阶最后一跳只有两种情况,跳一级或者跳两级,所以青蛙跳三级台阶总的方法数位: 青蛙跳至只剩一级台阶和只剩两级台阶的方法数之和 

即 F(n) = F(n-1) + F(n-2) 

或者,我们多写几种情况,也可以发现规律,方法数为 前一次方法数 + 后一次方法数:



int jumpFloor( int number )  {    
   if (number <= 0)    
       return 0;    
   else if (1 == number)    
       return 1;    
   else if (2 == number)    
       return 2;    
           
   return jumpFloor( number-2 ) + jumpFloor( number-1 );    
}


5.。。。

我们可以用 2*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2*1 的小矩形无重叠地覆盖一个 2*n 的大矩形,总共有多少种方法?

思路: 对于 n>=3 的情况,不管前面矩形是怎样覆盖的。我们只考虑最后一次怎么覆盖。

最后一次只有两种覆盖方式:1. 用一个小矩形竖着覆盖。2. 用两个小矩形横着覆盖。

所以总的方法数无外乎 --> 你用各种方法覆盖到只剩 1 个再竖着覆盖或者你用各种方法覆盖到只剩两个再横着覆盖

即:总的方法数 F(n) = n-1 次的方法数 F(n-1)(接着用一个小矩形竖着覆盖) + n-2 次的方法数 F(n-2)(接着用两个小矩形横着覆盖):


int rectCover( int number ) {  
   if ( number <= 0 )  
       return 0;  
         
   if ( 1 == number )  
       return 1;  
         
   if ( 2 == number )  
       return 2;  
     
   return rectCover( number-1 ) + rectCover( number-2 );  
}


6.

二进制中 1 的个数 输入一个整数,输出该整数二进制表示中 1 的个数

思路:一个整数减去 1 再和原整数做与行算,会把该整数二进制形式最右边一个 1 变为 0. 那么二进制表示有多少个 1 就可以进行多少次这样的操作。


int NumberOf1( int n ){  
   int count = 0;  
 
   while (0 != n){  
       ++count;  
       n = (n - 1) & n;  
   }  
 
   return count;  
}

7.合并两个排序的链表

ListNode* Merge( ListNode* pHead1, ListNoe* pHead2 )  {    
   if (NULL == pHead1)    
       return pHead2;    
   else if (NULL == pHead2)    
       return pHead1;    
   
   ListNode* pMergedHead = NULL;    
   
   if (pHead1->val < pHead2->val){    
       pMergeHead = pHead1;    
       pMergeHead->next = Merge( pHead1->next, pHead2 );    
   }    
   else{    
       pMergeHead = pHead2;    
       pMergeHead->next = Merge( pHead1, pHead2->next );    
   }    
   
   return pMergedHead;    
}

8.

从上往下打印二叉树 (层序遍历二叉树)

借助队列


void LevelPrint( Node* pRoot ){    
   if (NULL == pRtoot)    
       return;  
   
   queue<Node*> queueNode;    
   queueNode.push( pRoot );    
   
   while (0 != queueNode.size( ))  {    
       Node* pNode = queueNode.front( );    
       queueNode.pop( );  
   
       Visit( pNode );    
   
       if (NULL != pNode->pLeft)    
           queueNode.push(pNode->pLeft);    
   
       if (NULL != pNode->pRight)    
           queueNode.push(pNode->pRight);    
   }    
}



9.

数组中出现次数超过一半的数字


思路:如果有一个数字出现次数超过数组元素总个数的一半,则它出现次数比其他数字出现的次数总和还要多


bool g_inputInvalid = false;  
 
 
bool CheckInvalidArr( int* arr, int len ){  
   g_inputInvalid = false;  
 
   if (NULL == arr || len <= 0)  
       g_inputInvalid = true;  
 
   return g_inputInvalid;  
}  
 
 
bool CheckMoreThanHalf( int* arr, int len, int number ){  
   int times = 0;  
 
   for (int i = 0; i < len; ++i)  
       if (number == arr[i])  
           ++times;  
 
   bool isMoreThanHalf = true;  
   if (times * 2 <= len){  
       g_inputInvalid = true;  
       isMoreThanHalf = false;  
   }  
 
   return isMoreThanHalf;  
}  
 
 
int MoreThanHalfNum( int* arr, int len ){  
   // 如果只返回0存在二义性,是无效参数返回0还是0出现次数超过一半呢?  
   // 定义一个函数 和 一个全局变量,即可区分这两种情况  
   if (CheckInvalidArray( arr, len ))  
       return 0;  
 
   int result = arr[0];  
   int times = 1;  
 
   for (int i = 1; i < len; ++i){  
       if (0 == times){  
           result = arr[i];  
           times = 1;  
       }  
       else if (result == arr[i])  
           ++times;  
       else  
           --times;  
   }  
     
   // 此时result中保存的数字并不一定是数组中出现次数超过数组长度一半的数字,需要验证  
   // 11234  
   if (!CheckMoreThanHalf( arr, len, result ))  
       result = 0;  
 
   return result;  
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值