《剑指offer》【面试题4:替换空格】

面试题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()的话,会引起内存泄漏。(放在链表中空格替换也是一个道理)

想尝试的小伙伴,可以尝试一下


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值