QuickSort is a popular sorting technique based on divide and conquer algorithm. In this technique, an element is chosen as a pivot and the array is partitioned around it. The target of partition is, given an array and an element x of the array as a pivot, put x at its correct position in a sorted array and put all smaller elements (smaller than x) before x, and put all greater elements (greater than x) after x.
Multi-threading allows concurrent execution of two or more parts of a program for maximum utilization of CPU. Each part of such program is called a thread. So, threads are light-weight processes within a process.
Examples:
Input: arr[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}
Output: 1 2 3 4 5 6 7 8 9 10
Input: arr[] = {54, 64, 95, 82, 12, 32, 63}
Output: 12 32 54 63 64 82 95
Approach: The main idea of the approach is:
- The main thread calls the quicksort method.
- The method partitions the array and checks for the number of current threads.
- New threads is called for next step using the same parallel method.
- Use the single normal quicksort method.
Below is the program uses ForkJoinPool thread pool to keep number of thread same as the number of CPUs and reuse the threads:
C++
#include <iostream> #include <vector> #include <algorithm> #include <cstdlib> #include <ctime> #include <omp.h> using namespace std; class QuickSortMultiThreading { public : QuickSortMultiThreading( int start, int end, vector< int >& arr) : start_(start), end_(end), arr_(arr) {} // Finding random pivoted and partition // array on a pivot. // There are many different // partitioning algorithms. int partition( int start, int end, vector< int >& arr) { int i = start; int j = end; // Decide random pivot int pivoted = rand () % (j - i + 1) + i; // Swap the pivoted with end // element of array int t = arr[j]; arr[j] = arr[pivoted]; arr[pivoted] = t; j--; // Start partitioning while (i <= j) { if (arr[i] <= arr[end]) { i++; continue ; } if (arr[j] >= arr[end]) { j--; continue ; } t = arr[j]; arr[j] = arr[i]; arr[i] = t; j--; i++; } // Swap pivoted to its // correct position t = arr[j + 1]; arr[j + 1] = arr[end]; arr[end] = t; return j + 1; } // Function to implement // QuickSort method void operator() () { // Base case if (start_ >= end_) { return ; } // Find partition int p = partition(start_, end_, arr_); // Divide array QuickSortMultiThreading left(start_, p - 1, arr_); QuickSortMultiThreading right(p + 1, end_, arr_); // Left subproblem as separate thread #pragma omp parallel sections { #pragma omp section { left(); } #pragma omp section { right(); } } } private : int start_; int end_; vector< int >& arr_; }; int main() { int n = 7; vector< int > arr = {54, 64, 95, 82, 12, 32, 63}; srand ( time (NULL)); QuickSortMultiThreading(0, n - 1, arr)(); for ( int i = 0; i < n; i++) { // Print sorted elements cout << arr[i] << " " ; } cout << endl; return 0; } |
Java
// Java program for the above approach import java.io.*; import java.util.Random; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveTask; public class QuickSortMutliThreading extends RecursiveTask<Integer> { int start, end; int [] arr; /** * Finding random pivoted and partition * array on a pivot. * There are many different * partitioning algorithms. * @param start * @param end * @param arr * @return */ private int partition( int start, int end, int [] arr) { int i = start, j = end; // Decide random pivot int pivoted = new Random() .nextInt(j - i) + i; // Swap the pivoted with end // element of array; int t = arr[j]; arr[j] = arr[pivote]; arr[pivote] = t; j--; // Start partitioning while (i <= j) { if (arr[i] <= arr[end]) { i++; continue ; } if (arr[j] >= arr[end]) { j--; continue ; } t = arr[j]; arr[j] = arr[i]; arr[i] = t; j--; i++; } // Swap pivoted to its // correct position t = arr[j + 1 ]; arr[j + 1 ] = arr[end]; arr[end] = t; return j + 1 ; } // Function to implement // QuickSort method public QuickSortMutliThreading( int start, int end, int [] arr) { this .arr = arr; this .start = start; this .end = end; } @Override protected Integer compute() { // Base case if (start >= end) return null ; // Find partition int p = partition(start, end, arr); // Divide array QuickSortMutliThreading left = new QuickSortMutliThreading(start, p - 1 , arr); QuickSortMutliThreading right = new QuickSortMutliThreading(p + 1 , end, arr); // Left subproblem as separate thread left.fork(); right.compute(); // Wait until left thread complete left.join(); // We don't want anything as return return null ; } // Driver Code public static void main(String args[]) { int n = 7 ; int [] arr = { 54 , 64 , 95 , 82 , 12 , 32 , 63 }; // Forkjoin ThreadPool to keep // thread creation as per resources ForkJoinPool pool = ForkJoinPool.commonPool(); // Start the first thread in fork // join pool for range 0, n-1 pool.invoke( new QuickSortMutliThreading( 0 , n - 1 , arr)); // Print shorted elements for ( int i = 0 ; i < n; i++) System.out.print(arr[i] + " " ); } } |
Python3
# Python3 program for the above approach import random from concurrent.futures import ThreadPoolExecutor class QuickSortMultiThreading: def __init__( self , start, end, arr): self .start = start self .end = end self .arr = arr # Finding random pivoted and partition # array on a pivot. # There are many different # partitioning algorithms. # @param start # @param end # @param arr # @return def partition( self , start, end, arr): i = start j = end # Decide random pivot pivoted = random.randint(i, j) # Swap the pivoted with end # element of array t = arr[j] arr[j] = arr[pivoted] arr[pivoted] = t j - = 1 # Start partitioning while i < = j: if arr[i] < = arr[end]: i + = 1 continue if arr[j] > = arr[end]: j - = 1 continue t = arr[j] arr[j] = arr[i] arr[i] = t j - = 1 i + = 1 # Swap pivoted to its # correct position t = arr[j + 1 ] arr[j + 1 ] = arr[end] arr[end] = t return j + 1 # Function to implement # QuickSort method def __call__( self ): # Base case if self .start > = self .end: return # Find partition p = self .partition( self .start, self .end, self .arr) # Divide array left = QuickSortMultiThreading( self .start, p - 1 , self .arr) right = QuickSortMultiThreading(p + 1 , self .end, self .arr) # Left subproblem as separate thread with ThreadPoolExecutor(max_workers = 2 ) as executor: futures = [executor.submit(left), executor.submit(right)] for future in futures: future.result() # Driver Code if __name__ = = '__main__' : n = 7 arr = [ 54 , 64 , 95 , 82 , 12 , 32 , 63 ] QuickSortMultiThreading( 0 , n - 1 , arr)() for i in range (n): # Print shorted elements print (arr[i], end = ' ' ) |
C#
// C# program for the above approach using System; using System.Collections.Generic; using System.Threading.Tasks; class QuickSortMultiThreading { private int start; private int end; private int [] arr; private Random random; public QuickSortMultiThreading( int start, int end, int [] arr) { this .start = start; this .end = end; this .arr = arr; random = new Random(); } // Finding random pivoted and partition // array on a pivot. // There are many different // partitioning algorithms. // @param start // @param end // @param arr // @return private int Partition( int start, int end, int [] arr) { int i = start; int j = end; // Decide random pivot int pivoted = random.Next(i, j + 1); // Swap the pivoted with end // element of array int t = arr[j]; arr[j] = arr[pivoted]; arr[pivoted] = t; j -= 1; // Start partitioning while (i <= j) { if (arr[i] <= arr[end]) { i += 1; continue ; } if (arr[j] >= arr[end]) { j -= 1; continue ; } t = arr[j]; arr[j] = arr[i]; arr[i] = t; j -= 1; i += 1; } // Swap pivoted to its // correct position t = arr[j + 1]; arr[j + 1] = arr[end]; arr[end] = t; return j + 1; } // Function to implement // QuickSort method public void Sort() { if (start >= end) { return ; } int p = Partition(start, end, arr); QuickSortMultiThreading left = new QuickSortMultiThreading(start, p - 1, arr); QuickSortMultiThreading right = new QuickSortMultiThreading(p + 1, end, arr); List<Task> tasks = new List<Task>(); tasks.Add(Task.Run(() => left.Sort())); tasks.Add(Task.Run(() => right.Sort())); Task.WaitAll(tasks.ToArray()); } } // Driver Code class Program { static void Main( string [] args) { int n = 7; int [] arr = { 54, 64, 95, 82, 12, 32, 63 }; QuickSortMultiThreading quickSort = new QuickSortMultiThreading(0, n - 1, arr); quickSort.Sort(); for ( int i = 0; i < n; i++) { Console.Write(arr[i] + " " ); } } } // This code is contributed by shivhack999 |
Javascript
class QuickSortMultiThreading { constructor(start, end, arr) { this .start = start; this .end = end; this .arr = arr; } // Finding random pivoted and partition // array on a pivot. // There are many different // partitioning algorithms. partition(start, end, arr) { let i = start; let j = end; // Decide random pivot const pivoted = Math.floor(Math.random() * (j - i + 1)) + i; [arr[j], arr[pivoted]] = [arr[pivoted], arr[j]]; j--; // Start partitioning while (i <= j) { if (arr[i] <= arr[end]) { i++; continue ; } if (arr[j] >= arr[end]) { j--; continue ; } [arr[j], arr[i]] = [arr[i], arr[j]]; j--; i++; } [arr[j + 1], arr[end]] = [arr[end], arr[j + 1]]; return j + 1; } // Function to implement // QuickSort method operator() { if ( this .start >= this .end) { return ; } const p = this .partition( this .start, this .end, this .arr); const left = new QuickSortMultiThreading( this .start, p - 1, this .arr); const right = new QuickSortMultiThreading(p + 1, this .end, this .arr); const numThreads = 2; // Number of threads for parallel sections const promises = [ new Promise(resolve => resolve(left.compute())), new Promise(resolve => resolve(right.compute())) ]; return Promise.all(promises); } } const n = 7; const arr = [54, 64, 95, 82, 12, 32, 63]; const mainInstance = new QuickSortMultiThreading(0, n - 1, arr); await mainInstance.operator(); console.log(arr.join( ' ' )); |
12 32 54 63 64 82 95
Time Complexity: O(N*log N)
Auxiliary Space: O(N)