Open In App

Quick Sort using Multi-threading

Improve
Improve
Like Article
Like
Save
Share
Report

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: 
 

  1. The main thread calls the quicksort method.
  2. The method partitions the array and checks for the number of current threads.
  3. New threads is called for next step using the same parallel method.
  4. 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(' '));


Output

12 32 54 63 64 82 95 

Time Complexity: O(N*log N) 
Auxiliary Space: O(N)
 



Last Updated : 14 Sep, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads