Insertion Sort

Posted by Beetle B. on Tue 05 August 2014

An insertion sort starts from the first element in the list, and then for each element will check with the previous one to see if it is smaller than it. If so, it will repeatedly swap backwards until the element before it is not smaller.

The loop invariants are:

  1. When looking at the sublist from i+1 onwards, all the entries are greater than the sublist up to i.
  2. Everything to the left of the i-th element is sorted in ascending order.

To increment the loop, we increase i. If the invariant breaks, we take steps to restore the invariance. Those steps are the actual algorithm.

Complexity

The complexity is \(O(N^{2})\). In practice, it is about \(\frac{1}{4}N^{2}\) compared to \(\frac{1}{2}N^{2}\) of selection sort. The reason is that insertion sort, on average, only has to swap about half the number of entries backward to satisfy the invariant.

The best case scenario is when the list is already sorted. Then the algorithm takes linear time.

The worst case is when the list is sorted in the reverse order. Then the algorithm is effectively the selection sort.

So \(o(N)\) and \(O(N^{2})\).

Properties

  1. The sorting is in-place. No extra memory is needed, and is thus useful in tight memory situations.
  2. It is a stable sort.
  3. For a partially sorted list, the insertion sort performs in linear time. This is because the number of exchanges is the number of inversions.
  4. It can sort a list as it is received (also known as online).
  5. It requires more writes than selection sort, and may be at a disadvantage if writing is expensive.

Implementations

Python

def insertion_sort(lst, comparator=None):
    """
    Insertion sort. It takes in a list of objects and sorts them based
    on the comparator. It only works with a list.
    
    Keyword Arguments:
    lst        -- List to sort
    comparator -- Comparator function. It should take in two arguments, and
                  return -1 if the left is smaller, 1 if the right is smaller,
		  and 0 if they are equal. If none is provided, then it will
		  assume a numerical sort.
    """
    if not comparator:
	def comp(left, right):
	    if left < right:
		return -1
	    elif left > right:
		return 1
	    else:
		return 0
	comparator = comp

    for index in xrange(1, len(lst)):
	value = lst[index]
	previous = lst[index-1]
	offset = 0
	while comparator(value, previous) < 0:
	    lst[index - offset] = previous
	    lst[index - offset - 1] = value
	    offset += 1
	    if index - offset < 1:
		break
	    previous = lst[index - offset - 1]
	

C++

#include <algorithm>
#include <vector>


// Pass in the start and stop iterators to the vector.
void insertion_sort(std::vector<int>::iterator const & begin, 
                    std::vector<int>::iterator const & end);

void insertion_sort(std::vector<int>::iterator const & begin, 
                    std::vector<int>::iterator const & end)
{
  for(std::vector<int>::iterator iter = begin + 1; 
      iter != end; ++iter) 
    {
      int value = *iter;
      int previous = *(iter - 1);
      unsigned int offset = 0;
      while (value < previous) 
        {
          std::iter_swap(iter - offset, iter - offset -1);
          ++offset;
          value = *(iter - offset);
          previous = *(iter - offset - 1);
          if (iter - offset == begin)
            {
              break;
            }
        }
    }
}