The problem with inserting the element into the already-sorted array (aka Insertion Sort) is: While finding the index where to insert the element can be done in O(log n) using binary search, when actually inserting the element, all the following elements have to be shifted, resulting in (on average) O(n/2) for inserting into an array[]
or ArrayList
.
When using a LinkedList
, the elements do not have to be shifted, but here, you can't binary-search as well: Finding the index where to insert is about O(n/4) (according to this overview), and another O(n/4) for actually inserting the element, adding to O(n/2), same as for ArrayList
. (You could create a custom Skip List providing faster lookup and simultaneous insertion, but AFAIK Java does not provide something like this.)
If the numbers are unique, you could consider inserting them into a TreeSet
and then calling toArray
at the end. Inserting each number will be O(log n), for a total of O(n log n), with an additional O(n) each time you get the numbers in sorted order. (According to your comment, they are not unique, but maybe this helps others.) You could still use a variant of this approach using a TreeMap
, mapping elements to their count, but this will be more complex to implement.
Thus, collecting the numbers in an array[]
or ArrayList
or LinkedList
first and then sorting at the end seems to be better -- provided, of course, that you do not need the sorted version of the array in each step.
The "sorted insert" would give you (on average) O(log n + n/2) = O(n/2) for inserting each of the n numbers, for a total of O(n²/2), while maintaining a sorted array at all times. Sorting at the end is O(1) for inserting each of the n numbers plus O(n log n) at the end (or whenever you need the sorted list in between), resulting in O(n + k n log n) = O(k n log n) for sorting k > 0 times. (And if we solve for k, we see that sorting at the end would be faster as long as k < n / 2 log n.)