I have here two different recursive functions for reversing a string in 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);
}
Given a string of length 5000, this is the program’s output:
Input: ...
Length: 5000
Reverse 1:
9999 function calls
16 milliseconds
Reverse 2:
5000 function calls
52 milliseconds
Now why is the function with double the function calls ~3x faster? How should I be structuring my recursive functions for max speed in Java?
That’s a matter of good old algorithmic analysis. Your
reverse1should run in O(n logn) time, whereasreverse2needs O(n²) time, so the longer the string to reverse is, the more dramatically faster thanreverse2willreverse1be.The resource usage is dominated not by the number of calls, but by the time taken to copy characters into the new String object created at each string concatenation operation. The string concatenation in
reverse2works on longer strings on average than the one inreverse1, so its total execution time is longer.In
reverse1, every character is copied log2(n) times (where n is the length of the original string), since the depth of the recursive call tree is about log2(n).In
reverse2every character is copied a number of times equal to its position in the original string (±1, which I don’t care about). That makes n/2 copies on average of each character.For large n, log2(n) is much smaller than n/2, and therefore
reverse1tends to be faster.