面试题4:替换空格
字符串:一种基本的数据结构,是由若干字符组成的序列。注意的是每个字符串都以"\0"作为结尾,这样当我们在使用字符串的时候,也可以作为一个字符串的结尾标志。但是也是因为"\0"这个字符使得稍不留心就会造成字符串的越界,这也算是字符串的一个考点,或者认为是和数组的比较吧。
存储:把常量字符串放到单独的一个内存区域,当几个指针赋值给相同的常量字符串的时候,实际上这几个指针指向的是一块相同的内存地址。这也是指针与数组的区别吧。
面试题目:
请实现一个函数,把字符串中的每个空格替换成" "。例如:输入"We are happy.",则输出"We are happy."。
注:空格的ASCII是32,十六进制为0x20,因此替换成了" "。同样的"#"的ASCII值为35,即十六进制为0x23,在URL中被替换成"#".
分析:首先一个空格本来占一个格子数,替换之后是'%','2','0',占了三个格子数,所以字符串会变长,如果在以前的字符串上做替换则会覆盖空格后面的字符,所以我们需要为字符串申请足够大的内存空间。
内存空间解决完,接着说替换的思想是怎么替换的呢,最直接的就是从头到尾扫描字符串,每一次遇到空格,就做替换,并把数据向后移动两个格子数,为空格提供空间,但是这样的话,对于每个空格字符来说,都要把空格后面的字符向后移动一次,所以对于O(n)个空格字符来说,时间复杂度就达到了O(n^2)。这不是我们想要的,我们尽可能的把时间复杂度降到最低。
刚刚说了是从头到尾,我们换个方向再思考,当我们从后向前替换的时候,先把空格的数目计算出来,一次性把所有空格需要的空间大小给申请出来,这样也不会担心覆盖空格附近的字符。
我们从后向前移动和替换,首先定义两个指针(下标)用来标记,p1标记原始字符串的尾部('\0'),p2指向替换之后的尾部,这样两个指针进行标记的向前移动,边移边替换,最后当两个指针指向同一个位置时,说明空格
已经
替换完。
与上面的从头到尾比较:所有的字符只需要移动一次,相比较而言,时间复杂度也从O(n^2)降到了O(n),效率也会更高。
详细图解:
代码实现:
/*
时间复杂度为O(n);
空间复杂度为O(1);
*/
#include <stdio.h>
#include <assert.h>
void ReplaceBlank(char *str,int len)
{
assert(str != NULL);
if (str == NULL || len <= 0)
{
return ;
}
int black = 0;
int newlen = 0;
//遍历查看有多少个空格需要替换
while(str[newlen] != '\0')
{
if (str[newlen] == ' ')
{
black++;//记录空格数
}
newlen++;
}
int p1 = newlen;//指向没有替换前的'\0',类似于指针
newlen = newlen+black*2;//替换后的数组长度
int p2 = newlen;//指向替换后的'\0',类似于尾指针
if (newlen > len)
{
return ;
}
while(p1 >= 0 && p2 > p1)
{
if (str[p1] == ' ')//替换空格
{
str[p2--] = '0';
str[p2--] = '2';
str[p2--] = '%';
}
else//非空格进行移动
{
str[p2--] = str[p1];
}
p1--;
}
}
int main()
{
//字符串位于中间
char str[30] = "We are happy.";
ReplaceBlank(str,sizeof(str)/sizeof(str[0]));
printf("str :%s\n",str);
//字符串位于后面
char str1[30] = "We are happy. ";
ReplaceBlank(str1,sizeof(str1)/sizeof(str1[0]));
printf("str1 :%s\n",str1);
//字符串位于前面
char str2[30] = " We are happy.";
ReplaceBlank(str2,sizeof(str2)/sizeof(str2[0]));
printf("str2 :%s\n",str2);
//字符串有连续多个空格
char str3[30] = "We are happy.";
ReplaceBlank(str3,sizeof(str3)/sizeof(str3[0]));
printf("str3 :%s\n",str3);
//字符串没有空格
char str4[20] = "Wearehappy.";
ReplaceBlank(str4,sizeof(str4)/sizeof(str4[0]));
printf("str4 :%s\n",str4);
//空字符串
char str5[20] = "";
ReplaceBlank(str5,sizeof(str5)/sizeof(str5[0]));
printf("str5 :%s\n",str5);
//只有一个空格的字符串
char str6[20] = " ";
ReplaceBlank(str6,sizeof(str6)/sizeof(str6[0]));
printf("str6 :%s\n",str6);
//有只有连续多个空格的字符串
char str7[20] = " ";
ReplaceBlank(str7,sizeof(str7)/sizeof(str7[0]));
printf("str7 :%s\n",str7);
//字符串是NULL指针
char *str8 = NULL;
ReplaceBlank(str8,sizeof(str8)/sizeof(str8[0]));
printf("str8 :%s\n",str8);
return 0;
}
其实还有一种方法时间复杂度也是O(n),那就是每当遇到一个空格的时候,用malloc动态申请一片空间供使用,但是空间复杂度会变为O(n),以及malloc多次使用,没有free()的话,会引起内存泄漏。(放在链表中空格替换也是一个道理)
想尝试的小伙伴,可以尝试一下