Shubham Gautam

Quick Sort Algorithm

Quicksort is a prominent divide-and-conquer-based sorting algorithm renowned for its efficiency. Operating by selecting a pivot and partitioning the array around this pivot, Quicksort places the pivot in its correct sorted position. This technique achieves an average of nlogn comparisons for sorting n elements, making it notably swift.

The algorithm’s essence lies in its divide-and-conquer approach: it breaks down tasks into smaller subproblems, resolves them, and amalgamates the solutions. Initially, a pivot element is chosen, leading to the array’s partitioning into sub-arrays. One sub-array encompasses values less than or equal to the pivot, while the other contains values greater than it. Recursively, Quicksort sorts these sub-arrays. This partitioning and sorting continue until individual elements remain in each sub-array.

For students and professionals, understanding Quicksort’s fundamentals becomes pivotal, given its frequent examination appearances and its reputation as a rapid and efficient sorting method.

Working of Quick Sort Algorithm

Recall the list/array that had the elements – 10,8,5,15,610,8,5,15,6 in it. To sort these elements in ascending order using quick sort, let us follow the under-given steps –

Step – 1:

Select the pivot element, here for the sake of simplicity, we are selecting the rightmost element as the pivot.

How does quick sort work

Step – 2:

Rearrange the elements of the array in a way that all the elements smaller than the pivot are on its left and that all the greater ones are on the right. They need not be sorted. After the rearrangement, the array would look like this –

How does quick sort algorithm work

Here, 6 is our pivot element, and all the elements greater than itself are on the right side, while the elements smaller than the pivot is on its left side.

Now let us understand how did this rearrangement happen –

  • Take two pointer variables that point to the left & right of the array (with pivot excluded). Let’s call them left & right respectively. Working of quick sort
  • Under a loop, increment the left pointer until a value greater than pivot is found. In this case, since 10 is already greater than 6, we move to step c.
  • Under the same loop, increment the right pointer until a value smaller than pivot is found. Here, a value smaller than pivot is 5. Working of quick sort algorithm
  • Now since the conditions given in steps b and c are simultaneously fulfilled, swap the value at index left with the value at index right of the given array. This helps us move the elements smaller than the pivot element to the left of it, and the greater ones to the right of it. Quick Sort Algorithm Working Here, 5 & 10 are swapped.
  • Repeat steps b, c and d until the left index is greater than the right index.
  • Here, the execution of the loop is stopped because left > right. Now, swap the value at pivot with the value at the index left or rightSwapping in Quick Sort We have placed 6 after 5, and before 10, 15, and 8. This way, we have placed 6 (pivot element) at such a position that elements smaller than it is on its left, and the elements greater than it is on its right. Notice that 6 is at its correct sorted position now.

Step – 3:

In this step, the sublist is divided into two parts – sublist before the pivot element, and sublist after the pivot element.

Quick Sort-Dividing into Subarrays

Step – 4:

Repeat the above-given steps recursively until all the elements of the array are at their correct sorted position.

The following diagram shows the working of the quick sort algorithm.

Flowchart for working of quick sort algorithm

We have sorted the array using the quick sort algorithm. Let’s write some code to implement the algorithm.

Pseudocode for Quick Sort Algorithm

1. Pseudocode for Partitioning/Creating Subarrays –

//low and high are the lowest & highest index of the array/subarray respectively
function partition(array, low, high) {
  
   // selecting the rightmost element as pivot
   pivot = array[high]
  
   //setting the left pointer to point at the lowest index initially
   left = low
 
   //setting the left pointer to point at the lowest index initially
   right = high - 1;
 
   //running a loop till left is smaller than right
   while(left <= right)
 
       //incrementing the value of left until the value at left'th
     //index is smaller than pivot
       while(array[left] < pivot)
           left = left + 1
       end while
 
       //decrementing the value of right until the value at right'th
       //index is greater than pivot
       while(array[right] > pivot)
           right = right - 1
       end while
 
       if(left < right)
           //swapping the elements at left & right index
           swap(array[left], array[right])
       end if
 
   end while 
    
   // swapping pivot with the element where left and right meet
   swap(array[left], array[high])
   
   // return the partition point
   return left
 
end function

2. Pseudocode for Quick Sort Function

quickSort(array, low, high) {
  if low < high
    
     // since this function returns the point where the array is
     //partitioned, it is used to track the subarrays/partitions in the
     // array
     pi = partition(array, low, high)
 
     // recursively calling the function on left subarray
     quickSort(array, low, pi - 1)
 
     // recursively calling the function on right subarray
     quickSort(array, pi + 1, high)
 
  end if
 
end quicksort function

Implementation of Quick Sort

1. C Program for Quick Sort

// function to swap the elements
void swap(int *a, int *b) {
   int temp = *a;
   *a = *b;
   *b = temp;
}
 
// finding the partition point & rearranging the array
int partition(int array[], int low, int high) {
  
   // selecting the rightmost element as pivot
   int pivot = array[high];
  
   //setting the left pointer to point at the lowest index initially
   int left = low;
 
   //setting the left pointer to point at the lowest index initially
   int right = high - 1;
 
   //running a loop till left is smaller than right
   while(left <= right){
 
       //incrementing the value of left until the value at left'th index is smaller than pivot
       while(array[left] < pivot){
           left++;
       }
 
       //decrementing the value of right until the value at right'th index is greater than pivot
       while(array[right] > pivot){
           right--;
       }
 
       if(left < right){
           //swapping the elements at left & right index
           swap(&array[left], &array[right]);
       }
   } 
    // swapping pivot with the element where left and right meet
   swap(array[left], array[high]);
    // return the partition point
   return (left);
}
 
void quickSort(int array[], int low, int high) {
   if (low < high) {
    
       // since this function returns the point where the array is partitioned, it is used to track the subarrays/partitions in the array
       int pi = partition(array, low, high);
 
       // recursively calling the function on left subarray
       quickSort(array, low, pi - 1);
 
       // recursively calling the function on right subarray
       quickSort(array, pi + 1, high);
   }
}

2. C++ Program for Quick Sort

// finding the partition point & rearranging the array
int partition(int array[], int low, int high) {
  
   // selecting the rightmost element as pivot
   int pivot = array[high];
  
   //setting the left pointer to point at the lowest index initially
   int left = low;
 
   //setting the left pointer to point at the lowest index initially
   int right = high - 1;
 
   //running a loop till left is smaller than right
   while(left <= right){
 
       //incrementing the value of left until the value at left'th index is smaller than pivot
       while(array[left] < pivot){
           left++;
       }
 
       //decrementing the value of right until the value at right'th index is greater than pivot
       while(array[right] > pivot){
           right--;
       }
 
       if(left < right){
           //swapping the elements at left & right index
           swap(array[left], array[right]);
       }
   } 
    // swapping pivot with the element where left and right meet
   swap(array[left], array[high]);
    // return the partition point
   return (left);
}
 
void quickSort(int array[], int low, int high) {
   if (low < high) {
    
       // since this function returns the point where the array is partitioned, it is used to track the subarrays/partitions in the array
       int pi = partition(array, low, high);
 
       // recursively calling the function on left subarray
       quickSort(array, low, pi - 1);
 
       // recursively calling the function on right subarray
       quickSort(array, pi + 1, high);
   }
}

3. Python Program for Quick Sort

# Quick sort in Python
 
# finding the partition point & rearranging the array
def partition(array, low, high):
 
   # selecting the rightmost element as pivot
   pivot = array[high];
  
   # setting the left pointer to point at the lowest index initially
   left = low;
 
   #setting the left pointer to point at the lowest index initially
   right = high - 1;
 
   #running a loop till left is smaller than right
   while (left <= right):
       #incrementing the value of left until the value at left'th
       # index is smaller than pivot
       while (array[left] < pivot):
           left = left + 1
 
       #decrementing the value of right until the value at right'th
       #index is greater than pivot
       while (array[right] > pivot):
           right = right - 1;
      
       if (left < right):
           #swapping the elements at left & right index
           array[left], array[right] = array[right], array[left]
      
   #swapping pivot with the element where left and right meet      
   array[left], array[high] = array[high], array[left]
 
   # return the partition point
   return left
 
# function to perform quicksort
def quickSort(array, low, high):
 if low < high:
 
   # since this function returns the point where the array is
   #partitioned, it is used to track the subarrays/partitions in the
   #array
   pi = partition(array, low, high)
 
   # recursively calling the function on left subarray
   quickSort(array, low, pi - 1)
 
   # recursively calling the function on right subarray
   quickSort(array, pi + 1, high)

4. Java Program for Quick Sort

class Quicksort {
 
 // method to find the partition position
 static int partition(int array[], int low, int high) {
  
   // selecting the rightmost element as pivot
   int pivot = array[high];
  
   //setting the left pointer to point at the lowest index initially
   int left = low;
 
   //setting the left pointer to point at the lowest index initially
   int right = high - 1;
 
   //running a loop till left is smaller than right
   while(left <= right){
 
       //incrementing the value of left until the value at left'th
       //index is smaller than pivot
       while(array[left] < pivot){
           left++;
       }
 
       //decrementing the value of right until the value at right'th
       //index is greater than pivot
       while(array[right] > pivot){
           right--;
       }
 
       if(left < right){
           //swapping the elements at left & right index
           int temp = array[left];
           array[left] = array[right];
           array[right] = temp;
       }
   } 
   
   // swapping pivot with the element where left and right meet
   int temp = array[left];
   array[left] = array[high];
   array[high] = temp;
    
   // return the partition point
   return (left);
 }
 
 static void quickSort(int array[], int low, int high) {
   if (low < high) {
 
      // since this function returns the point where the array is
      //partitioned, it is used to track the subarrays/partitions in 
      //the array
       int pi = partition(array, low, high);
 
       // recursively calling the function on left subarray
       quickSort(array, low, pi - 1);
 
       // recursively calling the function on right subarray
       quickSort(array, pi + 1, high);
   }
 }
}

Complexity Analysis of Quick Sort

1. Quick Sort Time Complexity

Best-Case:

The best-case occurs when the pivot is almost in the middle of the array, and the partitioning is done in the middle. So, let us assume that we have a total of n elements in the array, and we are dividing the array from the middle.

In this case, with each partition, we will have at most n/2 elements. And we have to perform the partition until only one element is left in each subarray. The following is the tree representation of the given condition

Quick Sort-Best Case Scenario

Notice that the above-given tree is a binary tree. And for a binary tree, we can know that its height is equal to logn. Therefore, the complexity of this part is O(logn).

Also, the time complexity of the partition() function would be equal to O(n). This is because, under the function, we are iterating over the array to swap rearranging the array around the pivot element.

Therefore, from the above results, we can conclude that the best-case time complexity for the quick sort algorithm is O(nlogn).

Worst Case:

The Worst-case occurs when either of the two partitions is unbalanced. This generally happens when the greatest or smallest element is selected as the pivot.

In this case, the pivot lies at the extreme of the array, and one subarray is always empty while the other contains n – 1 elements.

This way, in the first call, the array is divided into two subarrays of 0 & n – 1 elements respectively. For the second call, the array with n – 1 elements is divided into two subarrays of 0 & n – 2 elements respectively. This continues till only one element is left.

The time complexity, in this case, would be the sum of all the complexities at each step.

T(quicksort) = T(n) + T(n – 1) + T(n – 2) + …….. + 2 + 1

T(quicksort) = O((n * (n – 1))/2)

T(quicksort) = &O(n ^ 2)&

The following is the tree representation of this condition –

Quick Sort-Worst Case Scenario

2. Quick Sort Space Complexity

In quick sort, the time complexity is calculated on the basis of space used by the recursion stack. In the worst case, the space complexity is O(n) because in the worst case, n recursive calls are made. And, the average space complexity of a quick sort algorithm is equal to O(logn).

From sorting to searching, Java DSA is essential. Join our free Java DSA course and become a proficient programmer in these core concepts.

Conclusion

  • Quick Sort method sorts the elements using the Divide and Conquer approach.
  • It has an average O(nLogn) complexity.
  • It can be implemented in both recursive and iterative ways.
  • Quick Sort is in-place, cache-friendly, and also a tail-recursive algorithm

Takeaways

Quicksort partitions an array and then calls itself recursively twice to sort the two resulting subarrays.

Complexity of Quick sort

  • Time complexity – O(nlog(n))
  • Space complexity – O(n)

FAQs

Q: Where is Quick Sort Used?

A: Quick sort is used in separating the Nth smallest or the largest element from an array. Being a fast sorting algorithm, quick sort is used by various departments in information retrieval. It is used for numerical computing where the efficient algorithms use priority queues and in turn quick sort for achieving accuracy. It is used where fast searching and/or sorting is the priority.

Q: Is Quick Sort a Stable Algorithm?

A: An algorithm is called stable if the relative order of two equal elements is preserved. Therefore, quick sort is not a stable algorithm because ordering is done on the basis of the pivot’s position.

Q: Why Quick Sort Runs Faster Than Merge Sort?

A: Quick sort is an in-place algorithm. But in merge sort, a new temporary array is required to merge the sorted subarrays. Therefore, quick sort is faster than merge sort.

Moreover, in quick sort, the worst case can be avoided by randomly choosing an element as the pivot, and performing sorting around it. Thus, a properly implemented quick sort has a time complexity of O(nlogn) in the average case.

Q: Why Quick Sort is Preferred for Array?

A: Quick sort is cache-friendly, which means it does not create unnecessary buffers in the cache memory and slows the system down. It is because, when used for arrays, quick sort has a good locality of reference

Author