Search code examples
c++algorithmsortinginsertion-sort

Insertion sort or a variation of selection sort?


I have a a code snippet here. Tested it for a few cases, seems to work fine.

I have written the code in one go for insertion sort after learning the algorithm, but have a question as to whether this is really a traditional insertion sort?

I have a feeling it may be a variation(tweaked version) of selection sort which is the cause of my confusion.

Specifically this is the area of concern: (Given array a of n elements)

for(i=1;i<n;i++){
    for(j=0;j<i;j++){
        if(a[i] < a[j]){
            temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
    }      
} 

Also, are the number of comparisons or swaps more/less with this kinda approach?

Thanks in advance for the help.


Solution

  • The most direct answer to your question is yes, it is insertion sort. It is a very inefficient insertion sort, but it nonetheless insertion sort.

    Your code lacks the conclusive step of knowing that, once the location of an element is determined, comparisons can stop and a shift operation on the sorted sequence ensues to make a hole for the new element. Rather, you rely on your comparison loop to perform that shift for you, even when comparisons are no longer needed, which is not very efficient.

    That probably seemed a little confusing, so I'll elaborate against your code.

    • Your prospect element for each iteration of i is initially a[i].
    • You enumerate linearly over the already-sorted part of your sequence, looking for where a[i] belongs
    • Once you find the location (unless it is already where it belongs), you swap a[i] with the element a[j] that currently resides in your target.
    • From that point on, the original value of a[i] is now in-place in the sequence, but...
    • For the remainder of the sorted sequence, the swap-comparison is guaranteed to fire as true (hint: so why do it?) against whatever value is stored in a[i] because the values that previously succeeded it were already sorted. Therefore, a[i] is constantly replaced with the next value in the sorted sequence until it eventually holds the largest value, which is by-definition where it belongs.

    Therefore, yes, this is insertion-sort. It maintains a sorted sequence at the beginning of the whole that ever-expands with each primary iteration. And for each primary iteration the prospect element is "inserted" and the trailing elements are shifted down to make the available hole to do that.

    ... are the number of comparisons or swaps more/less with this kinda approach?

    Considerably more comparisons required with your approach. Each iteration is guaranteed a linear O(n) complexity, and there are n iterations. Therefore, you're guaranteed to have O(N^2) complexity for your comparisons, which is the plague of inefficient sorting algorithms. Not just worst-case; guaranteed.


    A C++ Insertion Sort

    That said, consider this

    template<typename Iter>
    void insertion_sort(Iter first, Iter last)
    {
        for (Iter it = first; it != last; ++it)
            std::rotate(std::upper_bound(first, it, *it), it, std::next(it));
    }
    

    That probably seems like Greek (no offense to the Greeks) if you just starting out in C++, but it utilizes two fundamental algorithms that make it surprisingly efficient: std::upper_bound and std::rotate.

    std::upper_bound operates on a sorted sequence. Taking advantage of that, it can utilize a binary search algorithm to locate the first element in the sorted sequence that is strictly greater than the prospect value (*it). Therefore, searching for the insertion point for a single prospect is O(logN), far better than a linear search of O(n).

    Once the insertion point is known, std::rotate is used to put the element in place by using the iterator of the insertion point. It effectively does this:

    0 1 2 3 5 6 4
            ^ ^ *  these will be rotated right one element
    
    0 1 2 3 5 6 
                4
    
    0 1 2 3   5 6 
            4
    
    0 1 2 3 4 5 6 
    

    Note that rotation requires no comparisons.

    Obviously this template solution isn't something someone will be submitting for some remedial algorithms course. But I hope it gives you some ideas on how insertion-sort can have its comparisons minimized by:

    • Using binary search on the already-sorted part of the sequence to minimize comparisons.
    • Use no comparisons when performing the rotation.