一、传统做法
从开始位置向后遍历,如果发现空格,则挪动后面的字符串腾出 2 个字符的空间,写入“%20”,直至到字符串末尾。
(灰色部分是每次要挪动的字符)
这种方法非常直观,但是性能较差。假设字符串长度为 n,其中空格数量为 m,那么时间复杂度大概为 O(m*n),简化为 m = n,那么时间复杂度就变成了 O(n^2) 。
二、高性能做法
为了提高性能,需要尽可能的减少后面字符不必要的挪动次数,即:最后一次就能将后面的字符挪动到最终的位置上,方法就是从后面开始遍历字符串。
为了达到上述目的,需要提前知道空格替换完之后最终的字符串大小。所以开始遍历一遍字符串,假设字符串长度为 n,空格数量为 m,那么空格替换完之后的字符串长度为 len = n + m * 2 。
下一步就是开始搬运字符和替换空格的过程:
假设有两个指针分别指向了原始字符串末尾位置(p1)和最终字符串的末尾位置(p2)。首先要从后向前遍历,查看 p1 指向的字符是否是空格,如果不是的话就将该字符挪到 p2 位置上,由此类推,p1 不断向字符开始位置遍历,直至结束。
上述方法字符串每个字符挪动一次就行了,故时间复杂度为 O(n) 。
三、测试
#include <iostream>
#include <string.h>
void Print(const char *const p);
void ReplayBlank(char *const data, const size_t &len);
void test(const char *const data, const size_t &len);
int main(int argc, char *argv[])
{
test("I love China", 13);
test(" I love China ", 15);
test("I_love_China", 13);
test("", 0);
test(" ", 1);
return 0;
}
void test(const char *const data, const size_t &len)
{
char *src = new char[len * 2]();
memcpy(src, data, strlen(data));
Print(src);
ReplayBlank(src, strlen(src) + 1);
Print(src);
std::cout << "----------------------" << std::endl;
delete []src;
return;
}
void Print(const char *const p)
{
if (p == nullptr)
return;
std::cout << p << std::endl;
return;
}
void ReplayBlank(char *const data, const size_t &len)
{
if (data == nullptr || len == 0)
return;
size_t blank_count = 0;
int i = 0;
while (i < len)
{
if (data[i] == ' ')
blank_count++;
i++;
}
if (blank_count == 0)
return;
size_t len_all = len + blank_count * 2;
char *p1 = data + len;
char *p2 = data + len_all;
for (i = 0; i <= len; ++i)
{
if (*(p1 - i) != ' ')
{
*p2 = *(p1 - i);
p2--;
}
else
{
*(p2--) = '0';
*(p2--) = '2';
*(p2--) = '%';
}
}
return;
}
结果:
I love China
I%20love%20China
----------------------
I love China
%20I%20love%20China%20
----------------------
I_love_China
I_love_China
----------------------
----------------------
%20
----------------------
注意,代码中要展现出判空、空字符串、没有空格、全是空格等情况的处理。
(SAW:Game Over!)