Insertion sort
Insertion sort is another elementary sorting algorithm whose idea is rather easy to understand. The algorithmic idea of insertion sort can be stated as follows:
Assume that the sub-array with the first
elements of the array is already sorted. In the first round, . Take the
:th element . Shift the last elements in the sorted sub-array prefix one step right until the correct position for is reached. Insert to that position. Now the sub-array of the first
elements is sorted. Repeat until the whole array is sorted.
In pseudo-code, insertion sort can be described as follows.
Please notice that, unlike in Introduction to Algorithms (Aalto access),
we start array indexing from
An implementation for integer arrays in Scala, as well as a generic version in C++, look quite similar:
def insertionSort(a: Array[Int]): Unit =
var i = 1
while i < a.length do
val element = a(i)
var j = i;
while j > 0 && element < a(j - 1) do
a(j) = a(j - 1)
j -= 1
a(j) = element
i += 1
template <typename T>
void insertionsort(std::vector<T> &array) {
const std::size_t n = array.length;
for(std::size_t i = 1; i < n; i++) {
T element = array[i];
std::size_t j = i;
while (j > 0 and element < array[j - 1]) {
array[j] = array[j - 1];
j--;
}
array[j] = element;
}
}
Example: Insertion sort
Some properties of the algorithm:
Only few indices and the element currently moved are stored outside the array at any point of time. Thus the amount of extra memory required is
and the algorithm works in place. It is stable because we always find the rightmost position where the currently considered element belongs to, meaning that it is never moved left past an equal element.
Running time analysis
In the following analyses it is assumed that both (i) the comparison of two elements, as well as (ii) the array accesses are constant-time operations.
In the best case, insertion sort runs in time
Example: Insertion sort best case
In the worst case, the running time of the algorithm is
the second element is moved 1 steps,
the third element is moved 2 steps,
and so on, and
the
:th element is moved steps.
Each move of
Example: Insertion sort worst case
What about average case performance?
Consider an array consisting of
How about the performance on a specific application? This depends on the inputs that the application provides. If the input is usually almost sorted already, insertion sort may work fine in practice. But if the inputs are usually in random order or in some order causing many elements to be moved long distances, we should probably consider some other algorithm.
Observe that insertion sort code is very “compact” (small constants) and the memory accesses are very local. Therefore, for small arrays, insertion sort can be quite competitive as we will see later.
If the element comparisons are expensive,
one could use binary search for finding the correct place for each insertion.
This causes number of comparisons to drop to
Experimental evaluation
The plot below compares the performance of the above Scala implementation of
insertion sort to Selection sort and
the sorted
method for Scala arrays.
As we can see,
insertion sort is somewhat faster than selection sort
on these input arrays consisting of random integers.
However, their performance scales similarly and
they are not that efficient as the algorithm in the sorted
method
on these inputs.