题目:请实现一个函数,把字符串中的每个空格替换成“%20”。
例如:输入“We are happy.”,则输出“We%20are%20happy.”
对于这个问题,初见,可能感觉会比较简单,但隐藏了很多陷阱,需要考虑全面。
陷阱1:由一个字符,替换为,三个字符,那么字符串肯定变长,需要考虑原字符串内存是否足够
陷阱2:空指针检查,不能忘记
陷阱3:时间复杂度很关键
最直观的做法(从前向后替换):从头到尾扫面字符串,每一次碰到空格字符的时候做替换。由于是把1个字符替换成3个字符,我们必须要把空格后面的所有字符都后移两个字节,否则就有两个字符被覆盖。
这样做的话,对于一个存在多个空格的字符串,很明显存在移动多次的字符。增加了时间复杂度。
这种做法代码如下:
/** 一般解法,时间复杂度O(n^2) */
void Replace_Blank2(char str[],int length)
{
/**检查空指针*/
if(NULL == str || length <= 0)
return;
int i, j, p1, p2;
int new_len;
int old_len;
i = 0;
j = 0;
while(str[i] != '\0')
{
if(str[i] == ' ')
j++;
i++;
}
old_len = i; // 原字符串的长度
new_len = old_len + j*2; // 新字符串的长度
if(new_len >= length) // 检查内存是否足够
return;
p1 = 0; // p1指向最开始
p2 = old_len; // p2指向结尾
while(str[p1] != '\0')
{
if(str[p1] != ' ') // 不为空格,指针向后移动一位
p1++;
else{ // 由于是从前向后替换,因此碰到空格需要做两件事,一是先移位,二是后替换
while(p2 > p1)
{
str[2+p2] = str[p2]; // 空格后面所有字符向后移动两位
p2--;
}
old_len = old_len + 2;
p2 = old_len; // p2再次指向最后
str[p1++] = '%'; // 空格替换
str[p1++] = '2';
str[p1++] = '0';
}
}
}
逆向思维做法:从后向前替换,先遍历整个字符串,得出空格总数,从而计算出替换后字符串的总长度。这样就知道了最后面的字符需要移动的总位数,就相当于将后面本来要移动多次的字符,一次性先移掉。然后从后向前替换空格。
这样所有字符都只需移动一次,时间复杂度大大降低。
如下图所示:
代码如下:
/** 时间复杂度O(n) */
void Replace_Blank(char str[],int length)
{
if(NULL == str || length <= 0)
return;
int i, j, p1, p2;
int len_of_str; // 变量名要简单明了
int num_of_blank;
int new_len_of_str;
i = 0;
j = 0;
while(str[i] != '\0')
{
if(' ' == str[i])
j++;
i++;
}
len_of_str = i;
num_of_blank = j;
new_len_of_str = len_of_str + num_of_blank*2;
if(new_len_of_str >= length)
return;
p2 = new_len_of_str;
p1 = len_of_str;
while(p1 >= 0 && p2 > p1)
{
if(str[p1] != ' ')
{
str[p2--] = str[p1--];
}
else{
str[p2--] = '0';
str[p2--] = '2';
str[p2--] = '%';
p1--;
}
//p1--;
}
}
主函数如下:(可以添加其他测试用例)
int main()
{
char str[20] = "We are happy.";
int len = sizeof(str);
Replace_Blank(str,len);
cout << str << endl;
return 0;
}
结果如下:
/*点滴积累,我的一小步O(∩_∩)O~*/