简体   繁体   中英

Insertion sort or a variation of selection sort?

I have aa 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.

This code is an insertion-sort implementation. Take a look at the inner loop:

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

Or simplified:

for(j = 0; j < i ; j++)
    if(a[i] < a[j])
        swap(a[i], a[j]);

Now we know that the subarray a[0:i - 1] is already sorted from the previous run of the outer loop. This is a logically golfed version of finding the index n at which we need to insert a[i] and pushing all elements with index in the range [n, i - 1] one index higher:

while(j < i && a[j] <= a[i])
    j++;

//insert a[i] at the appropriate index
int tmp = a[j];
a[j] = a[i];

//push all elements starting from index n one index further
for(; j < i; j++){
    int swap = tmp;
    tmp = a[j];
    a[j] = swap;
}

The logical equivalence to these two snippets of code is the following:
Up to the searched index n (insertion-index for a[i] ), no swaps will occur. Now we swap out a[i] and a[n] . From that point on a[i] would be equivalent to the tmp -variable in the above code. Since the rest of the array is still sorted up index i - 1 , we now swap out every element against it's previous element, which is currently stored at index i . Definitely some nicely golfed insertion-sort.

The outer loop is just the standard

for(i = 0; i < array_length; i++)
     insert a[i] at appropriate position

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 efficient 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 is 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 minimised by:

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

Best of luck.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM