0%

Java字符串翻转速度吹毛求疵之旅

今天小健问我一道程序设计题,来自LeetOJ(problem 344):

Write a function that takes a string as input and returns the string reversed.
Example: Given s = “hello”, return “olleh”.
Subscribe to see which companies asked this question

这道题目是说要你设计一个function,完成一个字符串的翻转。
我先写了一个Java的样例试试水:

代码清单 1:
1
2
3
4
5
6
7
8
9
10
public class Solution {
public String reverseString(String s) {
StringBuffer buf = new StringBuffer();
for(int i=s.length()-1;i>=0;i--){
buf.append(s.charAt(i));
}
return buf.toString();

}
}

原理是反着把这个string读到stringbuffer里面去,然后转化为string返回
结果,用了7ms,击败了7%的programmer.

改进一下,直接用stringbuffer里面的reserve方法:

代码清单 2:
1
2
3
4
5
public class Solution {
public String reverseString(String s) {
return new StringBuffer(s).reverse().toString();
}
}

5ms,提升了2ms,击败了20%的 Java submissions.

然后我们考虑了一下使用数组交替的方法,这样的话其实只需要遍历半个字符串的长度,然后进行两两交换.

代码清单 3:
1
2
3
4
5
6
7
8
9
10
11
public String reverseString(String s) {
char[] array = s.toCharArray();
char temp;
int stopflag = s.length() / 2;
for (int i = 0; i < stopflag ; i++) {
temp = array[s.length() - 1 - i];
array[s.length() - 1 - i] = array[i];
array[i] = temp;
}
return new String(array);
}

3ms,再提升2ms,但是注意了,下面的代码其实大致原理是一样的,但是你觉得那一种代码更快呢?

代码清单 4:
1
2
3
4
5
6
7
8
9
public String reverseString(String s) {
char[] array = s.toCharArray();
for (int i = 0; i < s.length() / 2; i++) {
char temp = array[s.length() - 1 - i];
array[s.length() - 1 - i] = array[i];
array[i] = temp;
}
return new String(array);
}

代码清单4的代码用时4ms,清单3用时3ms.
我们这1ms的差异来自哪里.
应该是来自于我们的内存读写.

清单4的代码在进行逻辑运算时,使用了s.length()的方法
在计算的时候会分配内存进行运算.
清单3的代码中有我们的这一步运算其实已经存放在内存中一块空间里面了
因此不需要再内存IO中分配空间,导致时间加长.

最后贴出两个2ms可以完成的代码:

代码清单 5:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Solution {
public String reverseString(String s) {
char[] array = s.toCharArray();
int front=0,back=array.length-1;
char temp;
while(front<back){
temp = array[front];
array[front] = array[back];
array[back] = temp;
front ++;
back --;
}
return new String(array);
}
}
代码清单 6:
1
2
3
4
5
6
7
8
9
10
11
public String reverseString(String s) {
char[] arr = s.toCharArray();
int half =arr.length/2;
char temp ;
for(int i = 0 ,j = arr.length-1; i <half ; i++,j--){
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
return String.valueOf(arr);
}

我们总结了一下,对于同样的算法思路,尽可能减少临时变量和在循环运算时逻辑判断的数学运算量、有效提高整个算法的效率

比如在代码清单6中,我们把char temp和s.length()/2运算移到了循环体外,
这样可以避免局部变量运算时建立内存运算堆栈的耗时

p.s 有的同学会说,就差5ms,有意思吗,嗯,细想一下,如果你是一个后端服务的工程师,当你并发量足够大时,把运算的量降低0.5ms,也就可以适当降低内存的存取和堆栈的分配,这样是可以有效地降低服务器的运算负载的。

O.o 今晚就写这么多吧,我要下班回去睡觉了.