替换空格(Java)

该博客探讨如何在Java中高效地将字符串中的空格替换为"%20"。作者首先介绍了直观但效率低下的O(n^2)解决方案,然后提出优化思路,通过预计算空格数量避免不必要的字符移动,实现O(n)时间复杂度。此外,还提到了使用Java内置的replace和replaceAll函数作为替代方法。最后,作者通过一道类似问题引发思考,讨论了从后往前处理数组以降低时间复杂度的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

请实现一个函数,把字符串中的每个空格替换成“%20”。例如输入“We are happy.”,则输出“We%20are%20happy.”。

第一思路:

(1):是在原来的字符串上做替换(一定要保证内存够用)还是在新创建的字符串并在新的字符串上做替换。因为“ ”替换成“%20”字符串的长度会增加;

(2):最直观的做法是O(n^2)的解法;从头到尾扫描字符串,每一次碰到空格的时候做替换。由于是把一个字符替换成3个字符,我们必须要把后面的所有字符都后移两个字节,否则就有两个字符被覆盖了。故假设字符串的长度是n,对每个空格字符,需要移动后面O(n)个字符,因此对含有O(n)个空格的字符串而言总的时间效率是O(n^2)。

最优解思路:

上面的做法会使时间复杂度达到O(n^2),于是需要提升上面的解题思路使其时间复杂度达到O(n^2);先遍历一遍字符串统计出字符串中空格的总长度,因每次替换一个空格长度+2;故所需的总长度为oldLenth + blankCount * 2;上次我们的时间复杂度达到n的平方,是因为每次替换一个空格,我们的后面的字符串都要移动造成的,故这次我们从后往前移动,使字符在第一次就到达自己的位置上,故这样做的话时间复杂度为O(n);

代码:

public static String replaceBlank(StringBuffer sb){
	int blankCount = 0;
	//先统计出空格的数量
	for (int i = 0; i < sb.length(); i++) {
		if(sb.charAt(i)== ' '){
			++blankCount;
		}
	}
	int oldLength = sb.length(); //得到原来字符串的长度
	int newLength = oldLength + blankCount * 2; //将空格替换为"%20"后的字符长度
	int oldIndex = oldLength - 1; //原来字符串的索引
	int newIndex = newLength - 1; //新字符串的索引
	sb.setLength(newLength);
	//一次遍历,替换其中的空格为"%20"
	while(oldIndex >= 0 && newIndex > oldIndex){
		if(sb.charAt(oldIndex)== ' '){
			sb.setCharAt(newIndex--, '0');
			sb.setCharAt(newIndex--, '2');
			sb.setCharAt(newIndex--, '%');
		}else{
			sb.setCharAt(newIndex--, sb.charAt(oldIndex));
		}
		--oldIndex;
	}
	return sb.toString();
}

第二种解法:

利用Java自己的函数replace(param, param, param)函数帮助我们替换字符串。我们只需要找到空格的位置即可

public static String replaceBlank(StringBuffer sb){
	for (int i = 0; i < sb.length(); i++) {
		char ch1 = sb.charAt(i);
		if(ch1 == ' '){
			sb.replace(i, i+1, "%20");
		}
	}
	return sb.toString();
}

replace函数的源码:其中count是计算str的长度

/**
     * Replaces the characters in a substring of this sequence
     * with characters in the specified <code>String</code>. The substring
     * begins at the specified <code>start</code> and extends to the character
     * at index <code>end - 1</code> or to the end of the
     * sequence if no such character exists. First the
     * characters in the substring are removed and then the specified
     * <code>String</code> is inserted at <code>start</code>. (This
     * sequence will be lengthened to accommodate the
     * specified String if necessary.)
     *
     * @param      start    The beginning index, inclusive.
     * @param      end      The ending index, exclusive.
     * @param      str   String that will replace previous contents.
     * @return     This object.
     * @throws     StringIndexOutOfBoundsException  if <code>start</code>
     *             is negative, greater than <code>length()</code>, or
     *             greater than <code>end</code>.
     */
    public AbstractStringBuilder replace(int start, int end, String str) {
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (start > count)
            throw new StringIndexOutOfBoundsException("start > length()");
        if (start > end)
            throw new StringIndexOutOfBoundsException("start > end");

        if (end > count)
            end = count;
        int len = str.length();
        int newCount = count + len - (end - start);
        ensureCapacityInternal(newCount);

        System.arraycopy(value, end, value, start + len, count - end);
        str.getChars(value, start);
        count = newCount;
        return this;
    }

第三种解法:利用Java已经封装好的函数

public static String replaceBlank2(String str){
	String str1 = str.replaceAll(" ", "%20");
	return str1;
}

replaceAll函数实现

public String replaceAll(String regex, String replacement) {
        return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}

测试:

public static void main(String[] args) {
	StringBuffer sb = new StringBuffer();
	sb.append("We are happy.");
	String str = replaceBlank1(sb);
	System.out.println(str);
}

总结:

这道编程题考察对字符串的编程能力;

考察分析时间效率的能力,知道时间复杂度的计算;

对内存的考察;

课后小思:

有两个排序的数组A1和A2,内存在A1的尾部有足够多的空间去容纳A2。实现一个函数是A2插入A1是有序的,且时间复杂度最小。最后解的思路还是从尾部开始比较A1和A2,并把较大的数字复制到A1的合适位置,即第一次都到达自己的最终位置。故这类题目合并两个数组,如果从前往后复制每个数字需要重复移动数字多次,那么我们可以考虑从后往前复制,这样时间复杂度就会大大降低。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值