我在这里有两个不同的递归函数用于在Java中反转字符串:
Long ms1 = System.currentTimeMillis();
String str1 = reverse1(str);
ms1 = System.currentTimeMillis() - ms1;
Long ms2 = System.currentTimeMillis();
String str2 = reverse2(str);
ms2 = System.currentTimeMillis() - ms2;
System.out.println("Input: " + str);
System.out.println(" Length: " + str.length());
System.out.println("Reverse 1:");
System.out.println(" " + herp + " function calls");
System.out.println(" " + ms1 + " milliseconds");
System.out.println("Reverse 2:");
System.out.println(" " + derp + " function calls");
System.out.println(" " + ms2 + " milliseconds");
}
public static String reverse1(String str){
herp++;
if(str.length() == 1) return str;
return reverse1(str.substring(str.length()/2)) + reverse1(str.substring(0, str.length()/2));
}
public static String reverse2(String str){
derp++;
if(str.length() == 1) return str;
return reverse2(str.substring(1)) + str.charAt(0);
}
给定一个长度为5000的字符串,这是程序的输出:
Input: ...
Length: 5000
Reverse 1:
9999 function calls
16 milliseconds
Reverse 2:
5000 function calls
52 milliseconds
现在为什么带有双重函数的函数调用〜3倍更快?我应该如何构建我的递归函数以获得Java中的最大速度?
答案 0 :(得分:7)
这是一个很好的旧算法分析问题。您的reverse1
应该在O(n logn)时间运行,而reverse2
需要O(n²)时间,因此要反转的字符串越长,速度就越快reverse2
reverse1
{1}}是。
资源使用不是由调用次数决定,而是由将字符复制到每个字符串连接操作中创建的新String对象所花费的时间。 reverse2
中的字符串连接平均值平均值 <{1>},因此其总执行时间更长。
在reverse1
中,每个字符都被复制log2(n)次(其中n是原始字符串的长度),因为递归调用树的深度约为log2(n)。 / p>
在reverse1
中,每个字符的复制次数等于其在原始字符串中的位置(±1,我不在乎)。这使得每个角色平均有n / 2个副本。
对于大n,log2(n)远小于n / 2,因此reverse2
往往更快。
答案 1 :(得分:2)
第一种类型的大约50%的呼叫在没有做任何工作的情况下结束,因为str.length() == 1
。这使得具有非平凡工作的呼叫数量大致相等。如果您在退出条件之后移动derp++
和herp++
来电,您将获得“非平凡”电话的数量,并且它们将是相等的。
其他通话也会更快,因为平均而言,它们会连接较短的字符串,弥补3倍的差异。
答案 2 :(得分:1)
@HenningMakholm的答案非常好,但我只想根据@cHao对迭代方法和不可变字符串的评论将其抛弃。这可能是最适合评论的,但我想要答案的空间空间......
public static String reverse3(String str){
StringBuilder sb = new StringBuilder();
int i;
for(i = str.length() - 1; i >= 0; i--) {
sb.append(str.charAt(i));
}
return sb.toString();
}
此迭代方法最后只创建一个不可变String
对象,并在O(n)
时间运行会产生以下结果:
Length: 5406
Reverse 1:
10811 function calls
59 milliseconds
5406 length correctness test
Reverse 2:
5406 function calls
126 milliseconds
5406 length correctness test
Reverse 3:
3 milliseconds
5406 length correctness test