替换空格
请实现一个函数,把字符串中的每个空格替换成“%20”,
例如输入“we are happy.”,则输出“we%20are%20happy.”
看到这个题目,我们可以想到原来一个空格字符,替换之后变成了’%’,’2’,’0’这三个字符,因此字符串会变长,如果是在原来的字符串上进行替换,就有可能覆盖修改字符串后面的内存。如果是创建新的字符串并在新的字符串上进行替换,那么我们就可以自己分配足够的内存。
本文采用第一种做法: 在原来的字符串上进行替换。
第一种思路:
## 从前向后替换 ##
我们可以从头到尾扫描字符串,每当遇见空格时,进行替换,由于我们把一个字符换成了三个字符,所有我们必须把空格之后的所有字符全部向后移两个字节,否则就有两个字节被覆盖了。
让我们来看看这种思路的代码是怎么实现的:
void ReplaceBlank(char *str, int len)//str为字符数组
//len为传入字符数组的总大小 而非字符个数
{
if(NULL == str || len <= 0) //检验传入的参数是否合格
return;
int originlength = 0;//字符串长度
int numberOfBlank = 0; //空格个数
char *s = str;
while(*s != '\0')
{
++originlength;
if(*s == ' ')
++numberOfBlank;
++s;
}
int newlength = originlength + numberOfBlank * 2;// 替换空格之后字符串的长度
if(newlength > len)//字符数组不能存入替换后的字符串
return;
int i = 0;
for(; i < originlength; ++i)
{
if(str[i] == ' ')
{
for(int j = originlength + 2; j - 2 > i; --j)
{
str[j] = str[j - 2];
}
str[i] = '%';
str[++i] = '2';
str[++i] = '0';
originlength = strlen(str); //将新的字符串的长度赋值给originlength
}
}
}
对于这种从前往后遍历的思路,替换第一个空格,后面的字符需要移动一次,替换第n个空格时,第n个空格后面的字符就移动了n次,总的时间效率是O(n2);
显然 ,上面的方法效率比较低,不能满足我们的要求
那么我们何不换种思路,从后往前替换。
第二种思路
从后往前替换
我们可以定义两个指针,p1指向原始字符串末尾,p2指向替换后的字符串末尾,从后向前遍历,把p1指向的字符依次赋给p2,直到p1指向空格,p1前移一位,p2之前插入%20,将p2向前移动3位,依此类推,知道p1和p2指向同一个位置,表示所有空格已经替换完毕。
同样,让我们来看看代码实现 :
void ReplaceBlank(char *str, int len)
{
if(NULL == str || len <= 0)
return;
int originlength = 0;
int numberOfBlank = 0;
char *s = str;
while(*s != '\0')
{
++originlength;
if(*s == ' ')
++numberOfBlank;
++s;
}
char *p1 = &str[originlength]; //原始字符末尾
int newlength = originlength + numberOfBlank * 2;
//替换后的字符串长度,不包括'\0'
char *p2 = &str[newlength];//替换后的字符末尾
if(newlength > len)
return;
while(p1 != p2)
//p1 p2指向相同的地址时,表明空格已经全部替换完成
{
if(*p1 == ' ')
{
--p1;
*p2-- = '0';
*p2-- = '2';
*p2-- = '%';
continue;
}
*p2-- = *p1--;
}
}
从后往前替换的思路,实现了所有字符只移动一次便完成了替换,因此这个算法的时间复杂度是O(n),比第一个思路要快,从时间角度考虑,我们使用从后往前替换是更优的替换算法。