Quick Sort in C++

Video Tutorial
FREE
Introduction to Sorting thumbnail
This video belongs to
Data Structures in C++ Course
13 modules
Certificate
Topics Covered

Introduction

As the name suggests, a quick sort algorithm is known to sort an array of elements much faster (twice or thrice times) than various sorting algorithms.

Quick sort uses the divide and conquer technique where we partition the array around an element known as the pivot element, which is chosen randomly from the array. We solve the partitions to achieve an ordered array. Due to this reason, it is also known as the Partition Exchange Algorithm. A similar algorithmn to Quick Sort is Merge Sort.

Working of Quick Sort Algorithm In C++

The quick sort algorithm works in a divide-and-conquer fashion :

  • Divide :-

Choose a pivot element first from the array. After that, divide the array into two subarrays, with each element in the left sub-array being less than or equal to the pivot element and each element in the right sub-array being greater. This will continue until only a single element is left in the sub-array.

  • Conquer :

Recursively, sort the two subarrays formed with a quick sort.

  • Merge :

Merge the already sorted array.

Working of Quick Sort Algorithm in C++

To discuss the working of the quick sort algorithm, let us take an example of an array:

  1. Select the pivot element:-

Firstly, we choose a pivot element from the element. There are many different ways of choosing a pivot element : * Pick the first element as pivot always. * Pick the last element as pivot always. * Pick a random element as a pivot. * Pick median element as pivot.

In this article, we will consider the last element of the array as the pivot element.

Select the Pivot element

  1. Rearrange the array:-

We will rearrange the array such that elements smaller than the pivot element will come before the pivot element, and elements larger than the pivot element will come after the pivot element. Hence pivot element will come to its actual position.

In the above example, elements [9,8][9,8] will come after [7][7], and [6,3,5,2][6,3,5,2] will come before [7][7].

Rearrange the array 1

We rearrange the array in the following way :

  • We initialize a pointer PIndex at index 0. This pointer will finally point to the pivot element's actual position.

Rearrange the array 2

  • Then we traverse the array till Pivot with a pointer. Let us say i and check for the following condition :

This way, we put all the smaller elements to the left of PIndex and all the larger elements to the right of the PIndex.

traverse the array till Pivot 1

traverse the array till Pivot 2

  • After array traversal and swapping, do PIndex= PIndex-1. This way, we rearranged the elements by partitioning the array into two subarrays- one to the left of the PIndex and one to the right of the PIndex.
  1. Divide Subarrays: We pass the pivot index's left and right subarray recursively to the same process followed above. Therefore, in the above example, we pass subarray - [0 to 3] with pivot element as arr[3]arr[3] and subarray - [5 to 6] with pivot element as arr[6]arr[6] as the argument to the quickSort function again.

Divide subarrays

Sorted Array: final Sorted Array

Implementation Of Quicksort In C++

  • Firstly, we initiate two pointers, lo and hi, to the leftmost and rightmost elements of the array.
  • Then, we call the partition function, which rearranges the array such that all the smaller elements are left to the pivot element and all the larger elements are right to the pivot element.

It returns the final pivot index PIndex in the range [lo, hi].

  • Recursively call the quicksort for the left subarray of Pindex and the right subarray of the Pindex.

Pseudo Code :

C++ Code :

Output :

Quick Sort Complexity

  1. Time Complexity
CaseComplexity
Best CaseO(nlog(n))O(n*log(n))
Average CaseO(nlog(n))O(n*log(n))
Worst CaseO(nn)O(n*n)
  • Worst-Case Complexity:

In quick sort, worst-case complexity arises when the array is already sorted and the chosen pivot element is either the array's last or first element.

For example, if the array is [1,2,3,4,5][1,2,3,4,5] and the pivot is chosen as the leftmost element, we always get the pivot index as the first element. If the pivot index is the first element of the sorted array, recursion stack branches in the 1:n11: n-1 ratio give rise to nn levels of the recursion tree.

Hence, Quicksort's worst-case time complexity is O(nn)O(n*n).

quick sort Worst-Case Complexity

  • Best-case complexity:

Arises when the pivot element is the middle element or close to the middle element in a quick sort. When the pivot index is found in the middle, the recursion branch spreads more evenly; hence, log(n)log(n) levels of the recursion tree are achieved.

Therefore, the best case time complexity is O(nlog(n))O(n*log(n)).

quick sort Best-case complexity

  • Average Case Complexity :

It occurs when the array elements are not sorted at all. In other words, the array is fully unordered.

In this case, the average case scenario is similar to the best-case scenario as the jumbled array also gives an evenly divided recursion stack. Hence, Quicksort's average case time complexity is O(nlog(n))O(n*log(n)).

  1. Space Complexity
CaseComplexity
Best CaseO(log(n))O(log(n))
Average CaseO(log(n))O(log(n))
Worst CaseO(n)O(n)

As the algorithm is in place, where we sort the array in the original array, space complexity arises only because of the recursion stack. So, there must be a call stack entry for every one of these function calls.

This means that with an array of n integers, at worst, there will be n stack entries created, hence O(n)O(n) worst-case space complexity.

The average case space complexity arises when we create log(n)log(n) call stack entries. Hence average space complexity is O(logn)O(log n).

Characteristics Of Quick Sort

  • A stable sorting algorithm :

Maintains the position of two equal elements relative to one another. Quick sort is not a stable algorithm because elements are swapped according to the pivot’s position (without considering their original positions).

  • Quick sort algorithm is an in-place algorithm, which means array elements are sorted in the original array. In other words, the array is modified in the original array itself.
  • Quick sort is an algorithm for quickly and efficiently sorting an array or list. Because the algorithm is always in place, quick sort is used when the space is limited.

3-Way Quick sort (Dutch National flag)

In simple quick sort, we partition the array around the pivot element and recurse for the left and the right subarrays.

So, when array contains redundant elements with multiple occurrences like arr[]=[1,1,2,4,2,1,1,2,4,1,1,4,4,4]arr []=[1,1,2,4,2,1,1,2,4,1,1,4,4,4] with chosen pivot as 1 then we will be setting only one occurrence of 1 to its actual position in one recursive call.

To avoid this complexity, we use the 3-way Quick Sort algorithm, which partitions the array arr[l..r] into 3 subarrays :

  • arr[l...i] : Left subarray with elements less than the pivot element.
  • arr[i+1...j-1] : Subarray with elements equal to the pivot element.
  • arr[j...r] : Right subarray with elements greater than the pivot element.

Through this, we set the actual position of all the occurrences of 1 in a single pass. Thus 3-way quick sort can be used when we have more than one redundant element in the array.

Pseudo Code :

  • partition(arr, left, right, i, j) :
  • quicksort(arr, left, right) :

Randomized Quick Sort

Sometimes it happens when choosing the rightmost element in the sorted array as a pivot, resulting in a worst-case time complexity of O(nn)O(n*n).

In such cases, we use randomized quick sort algorithm, which is nothing but choosing a random pivot element from the array. Here we are probably choosing a pivot near the center of the array, and when this happens, the recursion branches spread more evenly, making it fast to sort the array elements in log(n)log(n) time.

Pseudocode(Lomuto partitioning) :

Note :

  • Using random pivoting, we improve the expected or average time complexity to O(nlog(n))O(n * log(n)). The Worst-Case complexity is still O(nn)O(n * n).
  • In internal sorting, all the data to sort is stored in memory at all times while sorting is in progress. In external sorting, data is stored outside memory (like on secondary memory) and only loaded into memory in small chunks.

Quick Sort vs. Merge Sort

FeaturesQuick sortMerge sort
PartitionsThe array is split into two parts around a pivot element and can be partitioned into any ratio.The array is partitioned into two halves(n/2)(n/2).
Worst case complexityO(nn)O(n*n)- We need to make many comparisons in the worst-case scenario.O(nlogn)O(n * log n) – same as the average case scenario.
Data sets usageIt is not efficient for larger data sets.It is efficient for all the data sets irrespective of size.
Additional spaceInplace – no need for additional space.Not in place – needs additional space to store auxiliary array.
Sorting methodInternal – data is sorted in the main memory.External – external memory is used for storing data arrays.
EfficiencyFaster and efficient for small size array.Fast and efficient for larger array and smaller both.
StabilityNot stable as two elements with the same values will not be placed relatively in the same order.Stable as two elements with the same values will appear in the same order in the sorted array.
Arrays/linked listsMore preferred for arrays.Works well for linked lists.

Conclusion

  • Quick sort in c++ uses the divide and conquer technique to sort elements of the array much faster than other sorting algorithms. It is also known as the Partition Exchange Algorithm.

  • We sort them by choosing a pivot, rearranging the array around it, and then calling recursively for both left and right subarrays around the pivot.

  • Quicksort's best-case time complexity is O(nlog(n))O(n*log(n)). In the worst-case scenario, it becomes O(nn)O(n*n) when all the elements are already sorted. The worst-case space complexity is O(n)O(n) for quicksort.

  • It is an in-place algorithm where sorting is done in the original array (cache-friendly).

  • We use the 3-way quick sort technique for redundant array elements.

  • We improve the worst-case scenario of a simple, quick sort by choosing a random pivot from the array(randomized quicksort).

  • Quick sort in C++ does not always partition the array into two halves, whereas, in merge sort, the partition is always into two halves.

  • In the real world, quicksort is generally used in commercial computing, numerical computations, and information search.

  • It is an unstable algorithm that does not preserve the relative ordering of the same elements from the original array.