In this article, a hybrid of the Quicksort with Insertion Sort is discussed to achieve better performance.

 
A Hybrid Algorithm is an algorithm that combines two or more other algorithms that solve the same problem, either choosing one (depending on the data) or switching between them throughout the algorithm. This is generally done to combine each of the desired features so that the overall algorithm is better than the individual components.

 
Quicksort is one of the fastest sorting algorithms for sorting large data. When implemented well, it can be about two or three times faster than its main competitors, merge sort and heapsort. There have been various variants proposed to boost its performance. Most of them we have already discussed here. Let’s revisit a few important optimizations that can be applied to Quicksort.

Tail Recursion:

To make sure at most, O(log(n)) space is used for an input containing n elements, recur first into the partition’s smaller side, then use a tail call to recur into the other. As such, we successfully sort the array in a way that minimizes the recursive depth.

Hybrid with Insertion Sort:

When the total number of elements is below some threshold (perhaps ten elements), switch to a non-recursive sorting algorithm such as insertion sort that performs fewer swaps, comparisons, or other operations on such small arrays.

Instead of “many small sorts” optimization, we can stop when the total number of elements is less than some threshold k. Later, when the whole array has been processed, each element will be at most k positions away from its final sorted position. Now, if we perform insertion sort on it, it will take O(k.n) time to finish the sort, which is linear as k is a constant.

Practice this algorithm

 
Following is the C++ and Java programs to demonstrate the Quicksort’s performance with its hybrid with insertion sort (and using tail optimizations):

C++


Download  Run Code

Output:

The average time taken by non-optimized Quicksort: 0.0239415
The average time taken by optimized Quicksort: 0.0219416

Java


Download  Run Code

Output:

The average time taken by the non-optimized Quicksort: 18758732ns
The average time taken by the optimized Quicksort: 16959413ns

The optimal cut-off for switching from Quicksort to Insertion sort is taken as 10 in the above program. As demonstrated by running the optimized and non-optimized Quicksort 10 times on 1 million elements (in C++ version) and taking an average of time taken by each, we can say that the optimized version is much faster than the non-optimized Quicksort.

 
Note if the input contains many repeated elements, Quicksort exhibits poor performance even with optimizations discussed above. We can use an alternative linear-time partition routine (sometimes called the Dutch national flag problem) to solve this problem that separates the values into three groups: values less than the pivot, values equal to the pivot, and values greater than the pivot. We have discussed this approach here.

 
References:

1. https://en.wikipedia.org/wiki/Quicksort
2. https://en.wikipedia.org/wiki/Hybrid_algorithm