main
You only provided a single test case (hello world
). Assuming this was the code you gave to the interviewer and they expected you to give a few test-cases then this probably made you look bad. Functions like these are straightforward to develop testcase equivalence-classes for and you can implement an exhaustive test suite very quickly.
reverseString
Your reverseString
implementation is inefficient: Using a StringBuilder
without an initial capacity means reallocating the StringBuilder
's internal buffer numerous times if you're dealing with a long string. There are a variety of approaches that offer improvements on your implementation.
Note that in Java, array allocation is O(n)
because the JVM will zero-out all arrays, whereas in languages like C and C++ it's O(1)
(though the host OS will probably zero-out pages before allocating them to your host process). Given that StringBuilder
operations such as .toString()
and the StringBuilder(String)
constructor are both O(n)
you're probably better-off using a char[]
as working-space:
Also you should handle invalid input more predictably (e.g. return null only for null input, returning the input directly if it's an empty input or only 1 character long).
private static String reverseString(final String input) {
if( input == null ) return null;
final int len = input.length();
if( len == 0 || len == 1 ) return input;
final char[] chars = input.toCharArray(); // O(n)
for( int i = 0; i < chars.length / 2; i++ ) { // O(n/2), but because it does 2 element assignments ops per loop, it works out as O(n) anyway.
final char c = chars[i]; // or replace this with an XOR swap if you're feeling smartarsed.
chars[i] = chars[ len - i ];
chars[ len - i ] = c;
}
return new String( chars ); // O(n)
}
reverseSentence
Your reverseSentence
is also suboptimal. When writing fast code you can always replace split
with a simple finite-state-machine parser which iterates through an existing buffer without needing to reallocate strings - which is good for the GC as there will be a lot less objects to process.
You didn't provide an automated test case for reverseSentence
so I don't know if the code is meant to reverse the order of words in a string (while maintaining in-word character order) or reverse the character-order but maintain the word order. I'm assuming it's the former. I don't know how the function should handle punctuation and other non-letter/digit characters.
static String reverseSentence(final String input) {
final int len = input.length(); // O(1)
StringBuilder sb = new StringBuilder( len ); // O(n)
int lastSpace = len;
for( int i = len - 1; i >= 0; i-- ) { // O(n)
char c = input.charAt( i );
if( c == ' ' ) {
// copy the last word into sb
if( lastSpace < len ) sb.append(' '); // potentially O(n)
for(int w = i; w < lastSpace; w++ ) {
sb.append( input.charAt( w ) );
}
lastSpace = i;
}
}
return sb.ToString(); // O(n)
}
This approach takes O(4n)
time and at most O(n+2)
space. Whereas your implementation is O(5n)
time and O(4n+1)
space in the best case, see my annotated comments for why:
private static String reverseSentence(final String input){
final String[] wordsInInput = input.split(" "); // O(2n) time, O(n) space
StringBuilder sb = new StringBuilder();
for(int i = wordsInInput.length-1; i >=0; i--){ // O(n) time
sb.append(wordsInInput[i]);
sb.append(" ");
}
final String reversed = sb.toString(); // O(n) time, O(n) space
return reversed.trim(); // O(n) time, O(n) space
}
String.split
takes O(2n)
time and O(n)
space because it allocates a new character array/string ((On)
space) for each split chunk, which requires zeroing by the JVM (O(n)
time) and then populating with character data (O(n)
time again). You also didn't preallocate your StringBuilder
so you'll lose time if/when it reallocates internally.