题目三:替换空格
请实现一个函数,将一个字符串中的每个空格替换成“%20”。
例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
思路描述
如果是在原来的字符串上进行替换,就有可能覆盖修改在该字符串后面的内存;
如果是新创建的字符串并在新的字符串的上进行替换,那么我们可以自己分配足够的内存。
由于有两种不同的解决方案,我们应该向面试官问清楚,让他明确的高速我门它的需求。
思路一:暴力破解(创建新的字符串)
/**
* 暴力破解:遍历整个字符串逐项查找每个位置上字符是否等于 空字符;若为空字符则进行替换
* 运行时间:22ms
占用内存:9584k
* @param str
* @return
时间复杂度为O(N);空间复杂度为O(n)
*/
public static String replaceSpace1(StringBuffer str) {
//先把空格找出来,然后将空格处的位置进行替换;新生成一个对象
StringBuilder stringBuilder = new StringBuilder();
for(int i =0;i<str.length();i++){//遍历整个字符串
if(str.charAt(i)==' '){//为空则置换
stringBuilder.append("%20");
}else{
stringBuilder.append(str.charAt(i));
}
}
return stringBuilder.toString();
}
思路二:借助正则表达式
/**
* 使用正则表达式
* @param str
* @return
*/
public String replaceSpace2(StringBuffer str) {
return str.toString().replaceAll("\\s", "%20");
}
思路三:从前往后遍历移动元素(时间复杂度为O(N*n))
最直观的做法就是遍历字符串,遇到 空格字符 就将其后面元素 依次向后移动两个位置;
因为要有两个for循环,所以时间复杂度为O(n^2)
/**
在字符串的基础上进行操作:从前往后 时间复杂度为O(n^2)
*/
public static String replaceSpace3(StringBuffer str){
int num = str.length();//字符串中的数量。动要变化
//遍历所有字符
for(int i=0;i<num;i++){
//假如当前字符为空
if(str.charAt(i)==' '){
//当前空格字符之后的字符向后移动2位\
for(int x=0;x<2;x++) {
int t = str.length() - 1;//从最后一个字符开始移动
while (t > i) {
char value = str.charAt(t);
str.replace(t+1,t+2,value+"");//往后挪动一位
t--;
}
}
str.replace(i,i+1,"%");//将%20插入
str.replace(i+1,i+2,"2");
str.replace(i+2,i+3,"0");
}
}
return str.toString();
}
思路四:从后往前,使用指针,定位出空白字符的数量
遍历字符串,统计出字符串中空白字符的总数,并计算出替换之后字符的长度
从字符串的后面开始复制和替换;
2.1、首先准备两个指针:P1:指向原始字符的尾部;P2:指向新字符串的尾部
2.2、若p1指向的字符不为空,则将p1处的字符拷贝到p2处 p1–;p2–;
若p2指向的字符为空,则将p1向前移动一步p1–;将p2向前移动三个位置; 替换p2后的三个位置
循环上述过程直到p1==p2
/**
* 从后往前遍历 :
* 时间复杂度为O(n)
* @param str
* @return
*/
public static String replaceSpace4(StringBuffer str){
//先计算字符串中空字符的个数,然后为其分配内存 :原字符的长度+空格字符数量*2
int spaceCharNum = 0;
for(int i=0;i<str.length();i++){
if(str.charAt(i)==' '){//发现空格字符
//将空白字符数量+1
spaceCharNum++;
}
}
//设置替换后 字符串应为的长度
str.setLength(str.length()+2*spaceCharNum);
//定义两个指针,p1:指向原字符串的尾部
//p2:指向新生成的字符串的尾部
//多出来的空白字符适用来存储 %20的。
//从后往前遍历 :找到应该插入%20的位置
int p1 = str.length()-2*spaceCharNum-1;
int p2 = str.length()-1;
//遍历字符串,当p1==p2时结束遍历
//当p1不为空时,将p1位置的字符拷贝到p2处
//当p1为空时,将p1向前移动一位,将p2向前移动3位,并将p2后的位置赋值为20%
while(p1!=p2){//遍历字符串,当p1==p2时结束
if(str.charAt(p1)!=' '){ //不为空
str.replace(p2,p2+1,str.charAt(p1)+"");
p1--;//p1与p2指针向前移动
p2--;
}else{
p1--; //p1向前移动一个位置
p2 -=3;//p2向前移动三个位置
str.replace(p2+1,p2+4,"%20");
}
}
return str.toString();
}
在合并两个数组(包括字符串时),如果从前往后复制每个数字(或字符)则需要重复移动数字(或字符)多次,那么我们可以考虑从后往前复制,这样就减少移动的次数,从而提高效率
扩展:有序数组合并
两个排序数组 A1,A2* 内存在A1的末尾有足够的空余空间容纳A2.*
把A2中的数字插入到A1中并且所有排序是有序的
思路一:从前往后,遍历复制数字
/**
* 两个排序数组 A1,A2
* 内存在A1的末尾有足够的空余空间容纳A2.
* 把A2中的数字插入到A1中并且所有排序是有序的
* @param A1
* @param A2
* @return
*/
public static int[] sort(int[] A1,int[] A2){
for(int i=0;i<A2.length;i++){
int j=0;
for(j=0;j<A1.length;j++){
if(A2[i]<A1[j]){//找到插入位置 j(包括j)之后的位置后移 一个位置
System.arraycopy(A1,j,A1,j+1,10-j);
A1[j]=A2[i];
break;
}
}
//当前遍历的数据比A1中素有数据都打;直接将数据添加到数组末尾
if(j==A1.length){
A1[5+i-1]=A2[i];
}
}
return A1;
}
思路二:从后往前复制
public static void merge(int[] A1, int[] A2, int lengthA1, int lengthA2){
int indexA1 = lengthA1-1;//数组A1的末尾元素
int indexA2 = lengthA2-1;//数组A2的末尾元素
int indexMerged = lengthA1+indexA2-1;//合并之后的末尾元素;合并之后
while(indexA1>=0&&indexA2>=0){//当没有比较晚
if(A1[indexA1] >= A2[indexA2]){ //假如A1中的元素大
A1[indexMerged] = A1[indexA1]; //将A1中的从后往前数的位置赋值为 A1中的元素
indexMerged--; //指针迁移
indexA1--;
}else{ //假如A2中的元素大
A1[indexMerged] = A2[indexA2];
indexMerged--;
indexA2--;
}
}
while(indexA1>=0){ //假如A1中海油剩余的元素
A1[indexMerged] = A1[indexA1];
indexMerged--;
indexA1--;
}
while(indexA2>=0){ //A2中海油剩余的元素
A1[indexMerged] = A2[indexA2];
indexMerged--;
indexA2--;
}
}
小结:上述合并字符串 或者从前往后复制数字的核心 就是分配出更多内存空间;我们可以转换为从后往前进行操作,确定每一个位置上应该放到数据为什么??