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;
}