I'm pasting this text from an ebook I have. It says the complexity if O(n2) and also gives an explanation for it, but I fail to see how.
Question: What is the running time of this code?
public String makeSentence(String[] words) {
StringBuffer sentence = new StringBuffer();
for (String w : words) sentence.append(w);
return sentence.toString();
}
The answer the book gave:
O(n2), where n is the number of letters in sentence. Here’s why: each time you append a string to sentence, you create a copy of sentence and run through all the letters in sentence to copy them over If you have to iterate through up to n characters each time in the loop, and you’re looping at least n times, that gives you an O(n2) run time. Ouch!
Can someone please explain this answer more clearly?
Looks like O(n) to me (with
n
being the total number of letters in all the words). You're basically iterating over every character inwords
to append it into theStringBuffer
.The only way I could see this as being O(n^2) is if
append()
iterates all the contents in the buffer before appending any new characters. And it may actually do this occasionally if the number of characters exceeds the currently allocated buffer length (it has to allocate a new buffer, and then copy everything from the current buffer into the new buffer). But it won't happen on every iteration, so you still won't have O(n^2).At most you'd have O(m * n), where
m
is the number of times the buffer length is increased. And because theStringBuffer
will double its buffer size every time it allocates a larger buffer we can determine thatm
is roughly equal tolog2(n)
(actuallylog2(n) - log2(16)
, since the default initial buffer size is 16 instead of 1).So the real answer is that the book's example is O(n log n), and that you can get it down to O(n) by preallocating a
StringBuffer
with a capacity large enough to hold all of your letters.Note that in Java appending to a string using
+=
does exhibit the inefficient behavior described in the book's explanation, as it has to allocate a new string and copy all the data from both strings into it. So if you do this, it is O(n^2):But using
StringBuffer
should not generate the same behavior as in the above example. That's kind of one of the major reasons forStringBuffer
to exist in the first place.This seems to be a question of mislead, because I happened to read that book just now. This part of text in the book is a typo! Here is the context:
===================================================================
Question: What is the running time of this code?
Answer: O(n2), where n is the number of letters in sentence. Here’s why: each time you append a string to sentence, you create a copy of sentence and run through all the letters in sentence to copy them over. If you have to iterate through up to n characters each time in the loop, and you’re looping at least n times, that gives you an O(n2) run time. Ouch! With StringBuffer (or StringBuilder) can help you avoid this problem.
=====================================================================
Have you noticed that the author messed it up? The O(n2) solution she mentioned (the first one) was exactly the same as the 'optimized' one (the latter). So, my conclusion is that the author was trying to render something else, such as always copying the old sentence to a new buffer when appending every next string, as the example of an O(n2) algorithm. StringBuffer should not be so silly, as the author also mentioned 'With StringBuffer (or StringBuilder) can help you avoid this problem'.
I think these text in the book must be a typo ,I think the right content is below,I fix it:
===================================================================
Question: What is the running time of this code?
Answer: O(n2), where n is the number of letters in sentence. Here’s why: each time you append a string to sentence, you create a copy of sentence and run through all the letters in sentence to copy them over. If you have to iterate through up to n characters each time in the loop, and you’re looping at least n times, that gives you an O(n2) run time. Ouch! With StringBuffer (or StringBuilder) can help you avoid this problem.
=====================================================================
Am i right?
That really depends on the implementation of
StringBuffer
. Supposing.append()
was constant time, it's clear that you have anO(n)
algorithm in time wheren = length of the words array
. If.append
isn't constant time, you'll need to multiple your O(n) by the time complexity of the method. If indeed the current implementation ofStringBuffer
copies strings character-by-character, then the algorithm above isΘ(n*m)
, orO(n*m)
, wheren
is the number of words andm
is average word length, and your book is wrong. I assume you're looking for a strict bound.Simple example that the book's answer is incorrect:
String[] words = ['alphabet']
By the book's definition,n=8
, so the algorithm will be bounded by 64 steps. Is this the case? Clearly not strictly. I see 1 assignment and 1 copy operation with n characters, so you get about 9 steps. This sort of behavior is predicted by the bounds ofO(n*m)
, as I illustrated above.I did some digging, and this clearly isn't a simple character copy. It looks like memory is being copied in bulk, which puts us back at
O(n)
, your first guess at the solution.Your book is either old, terrible, or both. I'm not determined enough to dig through JDK versions to find a less optimal implementation of StringBuffer, but perhaps one exists.