Given an integer array, sort it using the Quicksort algorithm.

Quicksort Overview

Quicksort is an efficient in-place sorting algorithm, which usually performs about two to three times faster than merge sort and heapsort when implemented well. Quicksort is a comparison sort, meaning that it can sort items of any type for which a less-than relation is defined. In efficient implementations, it is usually not a stable sort.

Quicksort, on average, makes O(n.log(n)) comparisons to sort n items. In the worst-case, it makes O(n2) comparisons, though this behavior is very rare.

How Quicksort Works?

Quicksort is a Divide and Conquer algorithm. Like all divide-and-conquer algorithms, it first divides a large array into two smaller subarrays and then recursively sort the subarrays. Basically, three steps are involved in the whole process:

  • Pivot selection: Pick an element, called a pivot, from the array (usually the leftmost or the rightmost element of the partition).
  • Partitioning: Reorder the array such that all elements with values less than the pivot come before the pivot. In contrast, all elements with values greater than the pivot come after it. The equal values can go either way. After this partitioning, the pivot is in its final position.
  • Recur: Recursively apply the above steps to the subarray of elements with smaller values than the pivot and separately to the subarray of elements with greater values than the pivot.

The base case of the recursion is arrays of size 1, which never need to be sorted. The following diagram shows how we choose the leftmost element as pivot at each step of the Quicksort algorithm, partition the array across the pivot, and recur on two subarrays we get after the partition process:

Quicksort Algorithm
 

 
Please note that the pivot selection and partitioning steps can be made in several ways, and the choice of specific implementation schemes significantly affects the algorithm’s performance. We will discuss all that in detail later, but let’s now get to the coding part.

Practice this algorithm

Following is the C++, Java, and Python implementation of the Quicksort algorithm:

C++


Download  Run Code

Output:

-6 -3 1 2 3 5 6 8 9

Java


Download  Run Code

Output:

[-6, -3, 1, 2, 3, 5, 6, 8, 9]

Python


Download  Run Code

Output:

[-6, -3, 1, 2, 3, 5, 6, 8, 9]

Quicksort Performance

The worst-case time complexity of Quicksort is O(n2), where n is the size of the input. The worst case happens when the pivot happens to be the smallest or largest element in the list or when all the array elements are equal. This will result in the most unbalanced partition as the pivot divides the array into two subarrays of sizes 0 and n-1. If this repeatedly happens in every partition (say, we have sorted array), then each recursive call processes a list of size one less than the previous list, resulting in O(n2) time.

T(n) = T(n-1) + cn = O(n²)

(Note – partition takes O(n) time that accounts for cn)

 
The best-case time complexity of Quicksort is O(n.log(n)). The best case happens when the pivot divides the array into two nearly equal pieces. Now each recursive call processes a list of half the size.

T(n) = 2 T(n/2) + cn = O(n.log(n))

 
The auxiliary space required by the Quicksort algorithm is O(n) for the call stack.

 
Must Read:

How to Boost QuickSort Performance?

 
References: https://en.wikipedia.org/wiki/Quicksort