013 以单词为单位的翻转字符串
最后更新于:2022-04-01 14:40:32
## 【我解C语言面试题系列】013 以单词为单位的翻转字符串
**以单词为单位的翻转字符串**
**原题**:Write a function string reverse string word By word (String input) that reverses a string word by word.
For instance:
"The house is blue" --> "blue is house The"
"Zed is dead" -->"dead is Zed"
"All-in-one" -->"All-in-one"
在不增加任何辅助数组空间的情况下,对于这个问题我们可以采用的办法就是:
**办法一**:
1、翻转整个字符串。
2、翻转每一个单词。
**办法二**:
1、翻转每一个单词。
2、翻转整个字符串。
办法一和二其实就是一个顺序的问题,并不影响算法的时间或空间复杂度。
下面给出代码:【本程序在DEV C++ 4.9.9.2 下编译通过】
~~~
#include <stdio.h>
#define IS_PRINT(ch)( (ch) > 0x20 && (ch) < 0x7E ) // except space
char * ReverseEveryWord(char *str);
char * ReverseWholeString(char * str);
char * LR_Reverse(char *left,char *right);
int main(void)
{
char str[] = "Hello word! **";
char *p = str;
#if 0
ReverseWholeString(str);
ReverseEveryWord(str);
puts(str);
#else
ReverseEveryWord(str);
ReverseWholeString(str);
puts(str);
#endif
system("pause");
return 0;
}
char * ReverseEveryWord(char *str)
{
char *right = str,*left = str;
if(str == NULL)
return NULL;
while( !IS_PRINT(*right) )
right++;
while(*right)
{
left = right;
while(IS_PRINT(*right))
right++;
LR_Reverse(left,right-1);
while(*right && !IS_PRINT(*right))
right++;
}
return str;
}
char * ReverseWholeString(char * str)
{
char *p = str;
if(str == NULL)
return NULL;
while(*p) p++;
p--;
LR_Reverse(str,p);
return str;
}
char * LR_Reverse(char *left,char *right)
{
char tt,*ret = left;
if(left == NULL || right == NULL)
return NULL;
while(left < right)
{
tt = *left;
*left++ = *right;
*right-- = tt;
}
return ret;
}
~~~
012 查找整数数组中第二大的数
最后更新于:2022-04-01 14:40:30
## 【我解C语言面试题系列】012 查找整数数组中第二大的数
**查找整数数组中第二大的数**
题目:写一个函数找出一个整数数组中,第二大的数。【Mirosoft】
PS:1、” 66,66,66,66,66 ”,则没有第二大数。
2、” 99,99,88,86,68,66 ”,则最大数是88。
下面我先给出查找最大数字的程序:
~~~
int GetFirstMaxNumber(int buffer[])
{
int i,max;
max = buffer[0];
for(i=1;i<ARRSIZE;i++)
{
if(buffer[i] > max)
max = buffer[i];
}
return max;
}
~~~
这个算法非常经典,时间复杂度是:O(N)。对于查找一个数组中的最大数字,我们至少要做的就是把数组扫描一遍,能在只扫描数组一遍的情况下就能解决问题的则算法已经是一个不错的算法的了。
查找第二大的数的算法就是在查找最大数的算法的基础上实现的,下面给出查找第二大数的程序:
~~~
#define ARRSIZE 10
#define MINNUMBER 0xFFFFFFFF
#define FIND_SUCESS 1
#define FIND_FAIL 0
int GetSecondMaxNumber(int buffer[],int *secondMax)
{
int i,max;
max = buffer[0];
*secondMax = MINNUMBER;
for(i=1;i<ARRSIZE;i++)
{
if(buffer[i] > max)
{
*secondMax = max;
max = buffer[i];
}
elseif (buffer[i] > *secondMax && buffer[i] < max)
*secondMax = buffer[i];
}
if(*secondMax == MINNUMBER) //The numbers are all the same.
return FIND_FAIL;
return FIND_SUCESS;
}
~~~
查找第二大数实际上是伴随在查找最大数的过程中的。
1、如果当前元素大于最大数 max,则让第二大数等于原来的最大数 max,再把当前元素的值赋给 max。
**2、**如果当前的元素大于第二大数secondMax的值而小于最大数max的值,则要把当前元素的值赋给 secondMax。――**判断条件不能仅仅只是大于第二大的数secondMax,否则我们便无法处理**” 99,99,88,86,68,66 ”**这种情况。**
PS:这个函数在调用时需要判断函数的返回值是否是 **FIND_SUCESS**才能使用。
011 删除指定字符串的相应字符
最后更新于:2022-04-01 14:40:28
## 【我解C语言面试题系列】011 删除指定字符串的相应字符
**删除指定字符串的相应字符**
假设字符串"cabcdefcgchci" ,那么要求你写一个函数,把该字符串中所有的字符 ’c’ 删除掉。那么结果应该是 "abdefghi"。
**00和01是用for循环来实现的算法**
~~~
char * DeleteChararcter_00(char *str,int c)
{
char *p,*ret;
for(ret= p = str;*p;p++)
{
if(*p == c)
continue;
*str++ = *p;
}
*str = '/0';
return ret;
}
char * DeleteChararcter_01(char *str,int c)
{
char *p,*ret;
for(ret= p = str;*p;p++)
{
if(*p != c)
*str++ = *p;
}
*str = '/0';
return ret;
}
~~~
**02和03是用while循环来实现的算法**
~~~
char * DeleteChararcter_02(char *str,int c)
{
char *p,*ret;
ret = p = str;
while(*p)
{
if(*p++ == c)
continue;
*str++ = *(p-1);
}
*str = '/0';
return ret;
}
char * DeleteChararcter_03(char *str,int c)
{
char *p,*ret;
ret = p = str;
while(*p)
{
if(*p != c)
*str++ = *p;
p++;
}
*str = '/0';
return ret;
}
~~~
010 从相应位置开始删除指定字符串的相应个字符
最后更新于:2022-04-01 14:40:25
## 【我解C语言面试题系列】010 从相应位置开始删除指定字符串的相应个字符
**从相应位置开始删除指定字符串的相应个字符**
假设一个字符串 " abcdefg ",那么请你写一个函数,该函数将会从指定位置开始,删除指定长度的字符。如:要从第二个开始,删除2两个字符。则删除后的字符串是“adefg”。
~~~
char *DeleteTheCharacters(char *str,int pos,int len)
{
char *p = str+pos-1;
int tt = strlen(str);
// If over the p && (p + len) over the length of str
if( pos < 1) return str;
if( (p-str) > tt ) return str;
if( (p+len-str) > tt)
{
*p = '/0';
return str;
}
//Delete the len characters
while(*p && *(p+len))
{
*p = *(p+len);
p++;
}
*p = '/0';
return str;
}
~~~
009 特殊的去除数组中重复数字问题
最后更新于:2022-04-01 14:40:23
## 【我解C语言面试题系列】009 特殊的去除数组中重复数字问题
**特殊的去除数组中重复数字问题**
有一个大小为101的数组,里面的数字均介于0到99之间,但是里面的数字仅有一个数字是重复的,请写个函数去除数组中的重复数字。
~~~
#define INIT_NUM -1
#define BUFFERSIZE 101
~~~
方法一:(最最容易想到的办法)
~~~
void RemoveBufferRepNum_00(int buffer[],int *num,int *loc)
{
int i,j;
for(i=0;i<100;i++)
{
for(j=i+1;j<101;j++)
{
if(buffer[i] == buffer[j])
{
*num = buffer[j];
*loc = j+1;
return;
}
}
}
}
~~~
这个算法最简单,时间复杂度是O(N2)
方法二:(采用hash表法解决)
~~~
void RemoveBufferRepNum_01(int buffer[],int *num,int *loc)
{
int tBuffer[BUFFERSIZE];
int i = 0,j = 0;
for(i=0;i<BUFFERSIZE;i++) //初始化数组
tBuffer[i] = INIT_NUM;
for(i=0;i<BUFFERSIZE;i++)//剔除算法
{
if(tBuffer[buffer[i]] == INIT_NUM)
tBuffer[buffer[i]] = buffer[i];
else
break;
}
*num = buffer[i];
*loc = i+1;
while(i < BUFFERSIZE-1)
{
buffer[i] = buffer[i+1];
i++;
}
buffer[i] = INIT_NUM;
}
~~~
这个办法是用开辅助空间,设置hash表来实现的,总共执行N次就可以了。时间复杂度是:O( N )。但是唯一的弱点就是需要额外的空间。
方法三:(采用折半查找法)
~~~
void RemoveBufferRepNum_02(int buffer[],int *num,int *loc)
{
int i,j,low,high,left=0,right=0,value;
low = 0,high = BUFFERSIZE-2;
while(low <= high)//查找重复数字
{
value = (low + high)/2;//low + ((high - low)/2);
for(i = 0;i<BUFFERSIZE;i++)
{
if( buffer[i] > value)
right++;
if( buffer[i] < value)
left++;
}
if( (right == (BUFFERSIZE-2 - value)) && (left == value) )
break;
elseif(right > (BUFFERSIZE-2 - value))
{
low = value;
right = 0;
left = 0;
}
elseif(left > (value-0))
{
high = value;
right = 0;
left = 0;
}
}
j = 0;
for(i = 0;i<BUFFERSIZE;i++)//扫描数组,找到重复数字所在的两个位置
{
if(buffer[i] == value)
j++;
if(j == 2)
break;
}
*num = buffer[i];
*loc = i+1;
while(i < BUFFERSIZE-1)
{
buffer[i] = buffer[i+1];
i++;
}
buffer[i] = INIT_NUM;
}
~~~
这个题目很特殊,数组大小为101,而所有的数字范围是0~99,只有一个是重复的。这里我们就可以采用折半的思想来解决(对于一般的要去掉多个重复数字的情况未必有效)。0~99之间共有100个数字,只有一个重复。
我们可以猜测这个重复的数字是50(处于中间的数字),那么在0~49之间有50个数字,在51~99之间49个数字。如果有一边大于它所应该有的数字个数,那么这个重复数字就肯定在多出来一个那一边。然后再拿出一个中间数字来猜测,不断的去拿中间的数字来猜测,直到猜出那个重复的数字为止。
因为 100 大于 2的6次方,小于 2的7次方。所以我们猜测到这个重复数字的次数最多是7次,最后加上1次查找循环,最多是需要8次扫描数组。时间复杂度是:O( N * log2 N )。相对于方法一来说已经大大的降低了执行次数,相对于方法二来说执行次数是仅仅是log2 N倍,这已经是在不增加额外空间的前提下修改 O(N2) 级别算法的较理想办法了。
方法四:
~~~
void RemoveBufferRepNum_03(int buffer[],int *num,int *loc)
{
int i,j,tt;
for(i=0,tt=0;i<BUFFERSIZE;i++)
tt += buffer[i];
tt -= 4950;
for(i=0,j=0;i<BUFFERSIZE;i++)//扫描数组,找到重复数字所在位置
{
if(buffer[i] == tt)
j++;
if(j == 2)
break;
}
*num = buffer[i];
*loc = i+1;
while(i < BUFFERSIZE-1)
{
buffer[i] = buffer[i+1];
i++;
}
buffer[i] = INIT_NUM;
}
~~~
本算法是经过网友的提醒,采用的是求和取余的办法来得到多余数字的,这个算法太巧妙了,很好的利用了题目中所给的条件。
0+1+2+3+4+5+…+98+99 = 4950 。题目说的是多而且只多一个重复的数字,那么所有的数字相加求和后减去 4950,余下的那个数就是重复数字。
然后我们再扫描一遍数组,找到数字的位置即可,时间复杂度是:O( N )。一个不加任何辅助空间,效率高的算法。这个算法来自于一个网友的提醒,这里先谢谢这个网友了。
008 去除数组中重复数字问题
最后更新于:2022-04-01 14:40:21
## 【我解C语言面试题系列】008 去除数组中重复数字问题
**去除数组中重复数字问题**
有一个大小为100的数组,里面的数字均介于1到99之间,但是里面的数字有重复,请写个函数去除数组中的重复数字。
`#define INIT_NUM-1`
方法一:(最最容易想到的办法)
~~~
void RemoveBufferRepNum_00(int buffer[])
{
int i,j;
for(i=0;i<BUFFERSIZE;i++)
{
for(j = i+1;j<BUFFERSIZE;j++)
{
if(buffer[i] == buffer[j])
{
buffer[i] = INIT_NUM;
break;
}
}
}
for(i=0,j=0;i<BUFFERSIZE;i++)
{
if(buffer[i] == INIT_NUM)
continue;
buffer[j++] = buffer[i];
}
while(j < BUFFERSIZE)
buffer[j++] = INIT_NUM;
}
~~~
这个算法最简单,时间复杂度是O(N2)
方法二:(采用hash表法解决)
~~~
void RemoveBufferRepNum_01(int buffer[])
{
int tBuffer[BUFFERSIZE];
int i = 0,j = 0;
for(i=0;i<BUFFERSIZE;i++) //初始化数组
tBuffer[i] = INIT_NUM;
for(i=0;i<BUFFERSIZE;i++)//剔除算法
{
if(tBuffer[buffer[i]] == INIT_NUM)
tBuffer[buffer[i]] = buffer[i];
}
for(i=0;i<BUFFERSIZE;i++)
{
if(tBuffer[i] == INIT_NUM)
continue;
buffer[j++] = tBuffer[i];
}
while(j < BUFFERSIZE)
buffer[j++] = INIT_NUM;
}
~~~
这个办法是用开辅助空间,设置hash表来实现的,总共执行N次就可以了。时间复杂度是:O( N )。但是唯一的弱点就是需要额外的空间。
007 运算符优先级问题
最后更新于:2022-04-01 14:40:19
## 【我解C语言面试题系列】007 运算符优先级问题
**运算符优先级问题**
给出下面程序的运行结果:
~~~
int main()
{
if( 0 & 1 == 0)
printf("0 & 1 == 0/n");
else
printf("0 & 1 != 0/n");
if( 0 & 1 != 0)
printf("0 & 1 != 0/n");
else
printf("0 & 1 == 0/n");
system("pause");
return 0;
}
~~~
答案是:
0 & 1 != 0
0 & 1 == 0
而不是我们想象中的
0 & 1 == 0
0 & 1 == 0
== 和 != 运算符优先级要高于 &、^、|、&&、|| 运算符,所以,
if( 0 & 1 == 0) 相当于 if( 0 & (1 == 0) ) 执行else。
if( 0 & 1 != 0) 相当于 if( 0 & (1 != 0) ) 执行else。
这个面试题不是要求我们强记住运算符的优先级,而是因为这个问题让很多程序员想当然是这样,结果最后debug后,才发觉是自己吃了亏,程序运行结果并不是自己想要的结果。这仅仅是告诉C/C++程序员一个很容易犯错误的陷阱。
006 按位反转整数问题
最后更新于:2022-04-01 14:40:16
## 【我解C语言面试题系列】006 按位反转整数问题
**按位反转整数问题**
Write a C function to swap the bits of a unsigned int so that its bits become the mirror image of the char. MSBs become its LSBs, e.g. 0111100011110111 binary should become 1110111100011110 binary.
方法一:(最最容易想到的办法)
~~~
unsignedint ReverseBitsInWord00(unsignedint Num)
{
unsignedint ret = 0;
int i;
for(i=0;i<32;i++)
{
ret <<= 1;
ret |= Num & 1;
Num >>= 1;
}
return ret;
}
~~~
上面的程序通过每次取传入参数的最后一位( Num & 1),然后与要返回的结果相 “ 或 ”,把传入参数 Num 右移 1 位,要返回的结果左移一位,来实现数字反转的。
方法二: (对换)
~~~
unsignedint ReverseBitsInWord01(unsignedint Num)
{
unsignedint ret = 0;
int i;
for(i=0;i<16;i++)
{
ret |= (Num & (1 << i)) << (31-2*i);
ret |= (Num & (0x80000000 >> i) ) >> (31-2*i);
}
return ret;
}
~~~
上面的程序通过对换来实现的。
1、先找到低位,然后移动到对应的高位,再与要返回的结果 或 。
2、再找到高位,然后移动到对应的低位,再与要返回的结果 或。
3、循环,直至对换 16 次。
方法三: (分而治之)
~~~
unsignedint ReverseBitsInWord02(unsignedint Num)
{
Num = (Num & 0x55555555) << 1 | (Num >> 1) & 0x55555555;
Num = (Num & 0x33333333) << 2 | (Num >> 2) & 0x33333333;
Num = (Num & 0x0F0F0F0F) << 4 | (Num >> 4) & 0x0F0F0F0F;
Num = (Num & 0x00FF00FF) << 8 | (Num >> 8) & 0x00FF00FF;
Num = (Num & 0x0000FFFF) << 16 | (Num >> 16) & 0x0000FFFF;
return Num;
}
~~~
上面的程序采用的是分而治之的策略( divide and conquer strategy )。把反转32位的程序分别分解为反转 2 位、4 位、8位、16位来实现的。无论是对于要反转几位,他们的算法是类似的。
1、取低位,左移。
2、右移,取高位。
3、( 1、2 的结果) 或
方法四: (分而治之)
~~~
unsignedint ReverseBitsInWord03(unsignedint Num)
{
Num = (Num & 0x55555555) << 1 | (Num & 0xAAAAAAAA) >> 1;
Num = (Num & 0x33333333) << 2 | (Num & 0xCCCCCCCC) >> 2;
Num = (Num & 0x0F0F0F0F) << 4 | (Num & 0xF0F0F0F0) >> 4;
Num = (Num & 0x00FF00FF) << 8 | (Num & 0xFF00FF00) >> 8;
Num = (Num & 0x0000FFFF) << 16 | (Num & 0xFFFF0000) >> 16;
return Num;
}
~~~
这个程序采用的也是分而治之的策略( divide and conquer strategy )。把反转32位的程序分别分解为反转 2 位、4 位、8位、16位来实现的。无论是对于要反转几位,他们的算法是类似的。
1、取低位,左移。
2、取高位,右移。
3、( 1、2 的结果) 或
上面的四个程序,前两个是我写的,是一个按照常规思维方式写的代码。后面的两个来自于 Henry S.Warren 的 《Hacker's Delight》一书中的有关 Reversing Bits 的相关介绍。
005 按位反转字符问题
最后更新于:2022-04-01 14:40:14
## 【我解C语言面试题系列】005 按位反转字符问题
**按位反转字符问题**
Write a C function to swap the bits of a unsigned char so that its bits become the mirror image of the char. MSBs become its LSBs, e.g. 01111000 binary should become 00011110 binary.
方法一:(最最容易想到的办法)
~~~
unsignedchar ReverseBitsInChar00(unsignedchar Num)
{
unsignedchar ret = 0;
int i;
for(i=0;i<8;i++)
{
ret <<= 1;
ret |= Num & 1;
Num >>= 1;
}
return ret;
}
~~~
上面的程序通过每次取传入参数的最后一位( Num & 1),然后与要返回的结果相 “ 或 ”,把传入参数 Num 右移 1 位,要返回的结果左移一位,来实现数字反转的。
方法二: (对换)
~~~
unsignedchar ReverseBitsInChar01(unsignedchar Num)
{
unsignedchar ret = 0;
int i;
for(i=0;i<4;i++)
{
ret |= (Num & (1 << i)) << (7-2*i);
ret |= (Num & (0x80 >> i) ) >> (7-2*i);
}
return ret;
}
~~~
上面的程序通过对换来实现的。
1、先找到低位,然后移动到对应的高位,再与要返回的结果 或 。
2、再找到高位,然后移动到对应的低位,再与要返回的结果 或。
3、循环,直至对换 4 次。
方法三: (分而治之)
~~~
unsignedchar ReverseBitsInChar02(unsignedchar Num)
{
Num = (Num & 0x55) << 1 | (Num >> 1) & 0x55;
Num = (Num & 0x33) << 2 | (Num >> 2) & 0x33;
Num = (Num & 0x0F0F) << 4 | (Num >> 4) & 0x0F0F;
return Num;
}
~~~
上面的程序采用的是分而治之的策略( divide and conquer strategy )。把反转32
位的程序分别分解为反转 2 位、4 位来实现的。无论是对于要反转几位,他们的算法是类似的。
1、取低位,左移。
2、右移,取高位。
3、( 1、2 的结果) 或
方法四: (分而治之)
~~~
unsignedchar ReverseBitsInChar03(unsignedchar Num)
{
Num = (Num & 0x55) << 1 | (Num & 0xAA) >> 1;
Num = (Num & 0x33) << 2 | (Num & 0xCC) >> 2;
Num = (Num & 0x0F) << 4 | (Num & 0xF0) >> 4;
return Num;
}
~~~
这个程序采用的也是分而治之的策略( divide and conquer strategy )。把反转32
位的程序分别分解为反转 2 位、4 位来实现的。无论是对于要反转几位,他们的算法是类似的。
1、取低位,左移。
2、取高位,右移。
3、( 1、2 的结果) 或
上面的四个程序,前两个是我写的,是一个按照常规思维方式写的代码。后面的两个来自于 Henry S.Warren 的 《Hacker's Delight》一书中的有关 Reversing Bits 的相关介绍。
004 数组的循环右移问题
最后更新于:2022-04-01 14:40:12
## 【我解C语言面试题系列】004 数组的循环右移问题
**数组的循环右移**
【题目】有一个整数数组,现要求实现这个整数数组的循环右移。如:1,2,3,4,5 则循环右移两位后结果是:4,5,1,2,3。
**方法一:**(最最容易想到的办法)
~~~
void RightCircleShift_00(int buffer[],int shift)
{
int i,j,tt;
for(i=0;i<shift;i++)
{
tt = buffer[ARRSIZE-1];
for(j=ARRSIZE-1;j>0;j--)
buffer[j] = buffer[j-1];
buffer[0] = tt;
}
}
~~~
这个办法是用两个循环来控制,内循环每次向右移动一位,外循环则用来限制移动的位数。算法需要执行 ARRSIZE * ShiftValue次,时间复杂度是O( N2 )。
**方法二:**(由方法一得到的递归程序)
~~~
void RightCircleShift_01(int buffer[],int shift)
{
int *p,tt;
tt = *(buffer+ARRSIZE-1);
for(p = buffer+ARRSIZE-1;p > buffer;p--)
*p = *(p-1);
*buffer = tt;
shift--;
if(shift > 0)
RightCircleShift_00(buffer,shift);
}
~~~
这个程序跟方法一类似,区别就是它是用递归来实现的。同样需要执行ARRSIZE * ShiftValue次,时间复杂度也是O( N2 )。
**方法三**:
~~~
void RightCircleShift_02(int buffer[],int shift)
{
int *title,*r,*p;
if(shift == 0)
return;
shift = shift % ARRSIZE;
title = (int *)malloc(sizeof(int)*shift);
if( title == NULL )
return;
r = buffer + (ARRSIZE - shift);
memcpy(title, r, shift * sizeof(int));
p = buffer + shift;
memmove(p, buffer, (ARRSIZE - shift) * sizeof(int));
memcpy(buffer, title, shift * sizeof(int));
free(title);
}
~~~
这个算法需要移动位数大小的临时辅助空间。如需移动两位,则申请两个的空间,然后把从右边起的两个元素拷贝的临时辅助空间,然后前面的元素向后移动两位,最后再把临时空间里面的两个元素拷贝到前面的两位即可完成循环右移。需要执行 ARRSIZE次,时间复杂度是O( N )。
**方法四:
~~~
void RightCircleShift_03(int buffer[],int shift)
{
if(shift <= 0)
return;
if( (shift & 1) == 0)
{
BufferShiftEvenNumber(buffer,shift-1);
BufferShiftEvenNumber(buffer,1);
}
else
BufferShiftEvenNumber(buffer,shift);
}
void BufferRightShiftEvenNumber(int buffer[],int shift)
{
int i,j,tt,res;
res = buffer[0];
for (i=0,j=0; i<ARRSIZE; i++)
{
tt = res;
res = buffer[(j+shift)%ARRSIZE];
buffer[(j+shift)%ARRSIZE] = tt;
j = (j+shift) % ARRSIZE;
}
}
~~~
这个算法并不需要开额外的辅助空间,而且时间复杂度同样也是O( N )。BufferRightShiftEvenNumber函数是这个算法的核心,该函数只能实现每次向右移动奇数位元素。如果要移动的位数是偶数的时候就需要把该数分解为两个奇数,n = (n – 1) + 1 。
**方法五:**
~~~
void RightCircleShift_04(int buffer[],int shift)
{
shift %= ARRSIZE;
ReverseArray(buffer,1,ARRSIZE);
ReverseArray(buffer,1,shift);
ReverseArray(buffer,shift+1,ARRSIZE);
}
void ReverseArray(int buffer[],int start,int end)
{
int i,tt;
if(end > ARRSIZE)
return;
start -= 1;
end -= 1;
while(start < end)
{
tt = buffer[start];
buffer[start++] = buffer[end];
buffer[end--] = tt;
}
}
~~~
这个办法也是很不错,需要两次扫描数组即可,时间复杂度O(N)。这个办法跟方法四移动偶数个元素的情况一样,都是需要两次扫描数组。当然如果是移动奇数个元素的话,则不如方法四有效,方法四只需要扫描一次数组就可以了。
算法是网友[luodongshui](http://blog.csdn.net/luodongshui/)提出的:
1、先将整个数组反转。
2、然后再反转前shift个元素。
3、接着再反转后N-shift个元素。
003 死循环格式问题小结?
最后更新于:2022-04-01 14:40:10
## 【我解C语言面试题系列】003 死循环格式问题小结?
**死循环格式问题小结**
下面是几个"著名"的死循环:
(1)操作系统死循环;
(2)WIN32程序死循环;
(3)嵌入式系统软件死循环;
(4)多线程程序的线程处理函数死循环。
而有的时候我们在程序中也要使用死循环,只有当条件满足的时候,才可以break 退出死循环,继续下面的代码的执行。死循环的方案有两个:
~~~
while (1)
{
……
}
for ( ; ; )
{
……
}
~~~
第一种格式往往是我们的首选方案。
第二种格式则由于这个语法没有确切表达代码的含义,我们从for ( ; ; ) 看不出什么,只有弄明白for ( ; ; ) 在C语言中意味着无条件循环才明白其意。有的程序员更是把第二种格式写成了for ( ;1 ; ) ,更是迷惑人。我们不要求所有人的所有代码格式都统一,但是象这种情况,还是统一一点的比较好,因为这样读你代码的人会更舒服一些,可以增强程序员间的代码交流。死揪语法,狠钻牛角尖,代码写的乱七八糟,各有各的一套,那对于代码维护来说是要付出很大代价的。
在C程序中,特别是嵌入式程序中除主程序的死循环外,一般的人都建议不要使用死循环,因为一旦你的代码稍微出现小的失误,就会造成当机,这是做嵌入式的人最不愿意看到的,因为QA(质量测试部门)是绝对不允许这种程序通过测试走货的。但是,在有的时候我们又不可避免的要使用死循环,所以要视具体情况而定。
002 局部变量和全局变量小结?
最后更新于:2022-04-01 14:40:07
## 【我解C语言面试题系列】002 局部变量和全局变量小结?
**局部变量和全局变量小结**
**局部变量**
局部变量也称为内部变量。局部变量是在函数内作定义说明的。**其作用域仅限于函数内部**,离开该函数后再使用这种变量是非法的。
局部变量从存储方式上可分为动态(auto)存储类型和静态(static)存储类型。
动态存储类型的局部变量都是动态的分配存储空间,数据存储在动态存储区(栈)中。函数调用结束后自动释放,生存期是在声明该变量的函数执行过程。
静态存储类型的局部变量则是静态的分配存储空间,数据存储在静态存储区中。在程序整个运行期间都不释放,生存期贯穿于程序运行的整个过程。
函数中的局部变量,如不专门声明为static存储类别,默认都是动态地分配存储空间的,我们在平时的声明变量的过程中auto都是默认省略的。
**全局变量**
全局变量也称为外部变量,是在函数的外部定义的,它的**作用域为从变量定义处开始,到本程序文件的末尾**。全局变量全部存放在静态存储区,在程序开始执行时给全局变量分配存储区,程序行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不动态地进行分配和释放;
如果外部变量不在文件的开头定义,**其有效作用域只限于定义处到文件终**。
如果在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字extern对该变量作“外部变量声明”。表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。**其有效作用域就被拓展到从这个文件**extern**声明处到文件结束**。
如果在全局变量声明的时候,前面加上关键字static,那么其他文件就不能再访问和使用该变量,**其有效作用域只限于定义处到文件终**。
**局部变量能否和全局变量重名**
局部变量能和全局变量重名,但是局部变量会屏蔽全局变量。在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。
PS:**这对**extern**声明的全局变量也一样**。
001 static有什么用途?
最后更新于:2022-04-01 14:40:05
## 【我解C语言面试题系列】001 static有什么用途?
【题目】static有什么用途?
在网上流传很广的一个答案是:
1、限制变量的作用域
2、设置变量的存储域
我觉得这样答题是不妥当的,有点文不对题的感觉。
下面是我给出的答案:
static类型声明符在C语言里面主要有三个用途:
1、声明静态局部变量。
2、声明静态外部全局变量。
3、声明静态外部函数。
下面是我整理的有关上面三个用法的解释说明。另外网友xiaocai0001的《static用法小结》一文有更详细的解释,请参考。
[http://blog.csdn.net/xiaocai0001/archive/2006/04/14/662921.aspx](http://blog.csdn.net/xiaocai0001/archive/2006/04/14/662921.aspx)
**静态局部变量**(与auto对比)
**1、存储空间分配、作用域和生存期**
static分配在静态存储区,作用域仅仅限于声明该变量的函数内部。在程序
整个运行期间都不释放,生存期贯穿于程序运行的整个过程。
auto类型分配在栈上,属于动态存储类别,占动态存储区空间,作用域仅仅限于声明该变量的函数内部。函数调用结束后自动释放,生存期不过是在声明该变量的函数内部。
**2、赋初值时的处理方式**
static静态局部变量在编译时赋初值,即只赋初值一次;
auto自动变量赋初值是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。
**3、未赋初值时的处理方式**
如果在定义局部变量时不赋初值的话:
static静态局部变量,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。
auto自动变量,如果不赋初值则它的值是一个不确定的值。
**静态外部全局变量**
在C语言中static还用来声明静态外部全局变量,那么这个全局变量的作用域就被限制在本文件内部。
外部变量(即全局变量)是在函数的外部定义的,它的作用域为从变量定义处开始,到本程序文件的末尾。如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件终了。如果在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字extern对该变量作“外部变量声明”。表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。
而如果我们声明的全局变量不想被其他文件访问和使用又该怎么办?
那就是在声明的时候前面加上关键字static。
**静态外部函数**
在C语言中我们的函数默认都是全局的,也就是说你可以调用其他文件中的函数。在使用的时候,我们象前面一样在头文件中加上extern就可以了。但是有时候我们写的函数并不想让别的文件访问和调用,那么我们在声明函数的时候前面加上static就可以了。
使用内部函数的好处有二:
1、可以让某些内部函数不为人所能使用,而仅仅让调用者使用他能使用的东西,有利于保护代码。
2、不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。
前言
最后更新于:2022-04-01 14:40:03
> 原文出处:[我解C语言面试题系列](http://blog.csdn.net/column/details/interview.html)
作者:[ammana_babi](http://blog.csdn.net/ammana_babi)
**本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!**
# 我解C语言面试题系列
> 很多c语言面试题不仅能很好的检测一个程序员对c语言的理解,还能给予我们学习上带来很大的启发,在这里与大家一起分享下一些经典的c语言面试题目。