In this article, we will discuss the grail sort. Grail sort is a sorting algorithm that was introduced by Vladimir Yaroslavskiy. It is an efficient sorting algorithm for large data sets that have a lot of duplicate values. The name “grail” refers to the fact that the algorithm is based on the idea of finding a “holy grail” of sorting algorithms that is both fast and capable of handling large amounts of duplicates.
Example:
Input: [7, 7, 4, 1, 5, 3, 2]
Output: [1, 2, 3, 4, 5, 7, 7]
Approach: To solve the problem follow the below steps:
- Divide the input array into blocks of size sqrt(n), where n is the length of the input array.
- Sort each block using a comparison-based sorting algorithm.
- Merge the sorted blocks into a single sorted array using an algorithm similar to merge sort.
- Repeat steps 2 and 3 until all blocks have been merged into a single sorted array.
Working of above approach:
- Divide the array into blocks of size 1:
- [7] [7] [4] [1] [5] [3] [2]
- Merge adjacent blocks:
- [7, 7] [1, 4] [3, 5] [2]
- Rotate the remaining blocks:
- [7, 7] [4, 1] [5, 3] [2]
- Merge adjacent blocks:
- [1, 4, 7, 7] [2, 3, 5]
- Divide the array into blocks of size 2:
- [1, 4] [7, 7] [2, 3] [5]
- Merge adjacent blocks:
- [1, 4, 7, 7] [2, 3, 5]
- Divide the array into blocks of size 4:
- [1, 4, 7, 7, 2, 3, 5]
- (8) Merge adjacent blocks:
- [1, 2, 3, 4, 5, 7, 7]
Below is the implementation for the above approach:
C++
// C++ implementation for the above approach. #include <algorithm> #include <cmath> #include <iostream> #include <limits> #include <vector> using namespace std; // grail sort for returning the sorted array vector< int > grailSort(vector< int > arr) { // Split the array into blocks of size sqrt(n) int blockSize = sqrt (arr.size()); int numBlocks = (arr.size() + blockSize - 1) / blockSize; vector<vector< int > > blocks(numBlocks); for ( int i = 0; i < numBlocks; i++) { blocks[i].resize(blockSize); copy(arr.begin() + i * blockSize, arr.begin() + (i + 1) * blockSize, blocks[i].begin()); // copying values from array to blocks // Sort the blocks using insertion sort for ( int j = 1; j < blockSize; j++) { int key = blocks[i][j]; int k = j - 1; while (k >= 0 && blocks[i][k] > key) { blocks[i][k + 1] = blocks[i][k]; k--; } blocks[i][k + 1] = key; } } // Merge the blocks using an algorithm // similar to merge sort and initialize // the pointers to the beginning of each block vector< int > pointers(numBlocks); vector< int > result; while ( true ) { // Find the minimum element among the // active blocks int minVal = numeric_limits< int >::max(); // minVal currently INT_MAX at start int minIdx = -1; for ( int i = 0; i < numBlocks; i++) { if (pointers[i] < blocks[i].size() && blocks[i][pointers[i]] < minVal) { minVal = blocks[i][pointers[i]]; minIdx = i; } } // If all blocks are exhausted, we're done if (minIdx == -1) { break ; } // Otherwise, add the minimum element // to the result and increment the // pointer for that block result.push_back(minVal); pointers[minIdx]++; } return result; } // Driver's code int main() { // Original Array vector< int > arr = { 7, 7, 4, 1, 5, 3, 2, 0 }; cout << "Input : " ; for ( auto x : arr) { cout << x << " " ; } cout << endl; // Printing result vector< int > result = grailSort(arr); cout << "Output: " ; for ( auto x : result) { cout << x << " " ; } cout << endl; return 0; } // Contributed by SR.Dhanush |
Java
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class GrailSort { // Grail sort for returning the sorted array public static List<Integer> grailSort(List<Integer> arr) { // Split the array into blocks of size sqrt(n) int blockSize = ( int )Math.sqrt(arr.size()); // Determine the block size as // sqrt(n) int numBlocks = (arr.size() + blockSize - 1 ) / blockSize; // Calculate the number // of blocks needed List<List<Integer> > blocks = new ArrayList<>( numBlocks); // Create a list to hold the blocks for ( int i = 0 ; i < numBlocks; i++) { blocks.add( new ArrayList<>(Collections.nCopies( blockSize, 0 ))); // Initialize each block with 0s Collections.copy( blocks.get(i), arr.subList(i * blockSize, Math.min((i + 1 ) * blockSize, arr.size()))); // Copy the elements // from the original // array to the // blocks // Sort the blocks using insertion sort for ( int j = 1 ; j < blockSize; j++) { int key = blocks.get(i).get(j); int k = j - 1 ; while (k >= 0 && blocks.get(i).get(k) > key) { blocks.get(i).set(k + 1 , blocks.get(i).get(k)); k--; } blocks.get(i).set(k + 1 , key); } } // Merge the blocks using an algorithm // similar to merge sort and initialize // the pointers to the beginning of each block List<Integer> pointers = new ArrayList<>(Collections.nCopies( numBlocks, 0 )); // Create a list to hold the pointers List<Integer> result = new ArrayList<>(); // Create a list to hold // the sorted result while ( true ) { // Find the minimum element among the // active blocks int minVal = Integer.MAX_VALUE; // Set the minimum // value as the maximum // possible integer int minIdx = - 1 ; for ( int i = 0 ; i < numBlocks; i++) { if (pointers.get(i) < blocks.get(i) .size() // Check if the // pointer is within // the bounds of the // block && blocks.get(i).get(pointers.get(i)) < minVal) { // Check if the value // at the pointer is // less than the // current minimum // value minVal = blocks.get(i).get( pointers.get(i)); minIdx = i; // Update the index of the // block containing the // minimum value } } // If all blocks are exhausted, we're done if (minIdx == - 1 ) { break ; } // Otherwise, add the minimum element // to the result and increment the // pointer for that block result.add(minVal); pointers.set(minIdx, pointers.get(minIdx) + 1 ); } return result; // Return the sorted result } // Driver's code public static void main(String[] args) { // Original Array List<Integer> arr = List.of( 7 , 7 , 4 , 1 , 5 , 3 , 2 , 0 ); System.out.print( "Input : " ); for ( int x : arr) { System.out.print(x + " " ); } System.out.println(); // Printing result List<Integer> result = grailSort(arr); System.out.print( "Output: " ); for ( int x : result) System.out.print(x + " " ); } } |
Python3
# Python code for implementation of the # above approach def grail_sort(arr): # Split the array into blocks # of size sqrt(n) block_size = int ( len (arr) * * 0.5 ) num_blocks = ( len (arr) + block_size - 1 ) / / block_size blocks = [arr[i * block_size:(i + 1 ) * block_size] for i in range (num_blocks)] # Sort each block using a # comparison sort for block in blocks: block.sort() # Merge the blocks using an algorithm # similar to merge sort # Initialize the pointers to the # beginning of each block pointers = [ 0 ] * num_blocks result = [] while True : # Find the minimum element among # the active blocks min_val = float ( 'inf' ) min_idx = None for i in range (num_blocks): if pointers[i] < len (blocks[i]) and blocks[i][pointers[i]] < min_val: min_val = blocks[i][pointers[i]] min_idx = i # If all blocks are exhausted, # we're done if min_idx is None : break # Otherwise, add the minimum # element to the result and # increment the pointer # for that block result.append(min_val) pointers[min_idx] + = 1 return result # Original Array arr = [ 7 , 7 , 4 , 1 , 5 , 3 , 2 , 0 ] print ( 'Input : ' , arr) # Printing result print ( "Output: " , grail_sort(arr)) |
C#
// C# implementation for the above approach. using System; using System.Collections.Generic; using System.Linq; class Program { // grail sort for returning the sorted array static List< int > GrailSort(List< int > arr) { // Split the array into blocks of size sqrt(n) int blockSize = ( int )Math.Sqrt(arr.Count); int numBlocks = (arr.Count + blockSize - 1) / blockSize; List<List< int > > blocks = new List<List< int > >(numBlocks); for ( int i = 0; i < numBlocks; i++) { blocks.Add( new List< int >(blockSize)); blocks[i].AddRange( arr.Skip(i * blockSize).Take(blockSize)); // copying values from array to blocks // Sort the blocks using insertion sort for ( int j = 1; j < blockSize; j++) { int key = blocks[i][j]; int k = j - 1; while (k >= 0 && blocks[i][k] > key) { blocks[i][k + 1] = blocks[i][k]; k--; } blocks[i][k + 1] = key; } } // Merge the blocks using an algorithm // similar to merge sort and initialize // the pointers to the beginning of each block List< int > pointers = new List< int >(numBlocks); for ( int i = 0; i < numBlocks; i++) { pointers.Add(0); } List< int > result = new List< int >(); while ( true ) { // Find the minimum element among the // active blocks int minVal = int .MaxValue; // minVal currently INT_MAX at start int minIdx = -1; for ( int i = 0; i < numBlocks; i++) { if (pointers[i] < blocks[i].Count && blocks[i][pointers[i]] < minVal) { minVal = blocks[i][pointers[i]]; minIdx = i; } } // If all blocks are exhausted, we're done if (minIdx == -1) { break ; } // Otherwise, add the minimum element // to the result and increment the // pointer for that block result.Add(minVal); pointers[minIdx]++; } return result; } static void Main( string [] args) { // Original Array List< int > arr = new List< int >{ 7, 7, 4, 1, 5, 3, 2, 0 }; Console.Write( "Input : " ); foreach ( int x in arr) { Console.Write(x + " " ); } Console.WriteLine(); // Printing result List< int > result = GrailSort(arr); Console.Write( "Output: " ); foreach ( int x in result) { Console.Write(x + " " ); } Console.WriteLine(); } } // This code is contributed by Susobhan Akhuli |
Javascript
// JavaScript code for implementation of the // above approach function grail_sort(arr) { // Split the array into blocks // of size sqrt(n) let block_size = parseInt(Math.sqrt(arr.length)); let num_blocks = Math.ceil(arr.length / block_size); let blocks = new Array(num_blocks); for (let i = 0; i < num_blocks; i++) { blocks[i] = arr.slice(i * block_size, (i + 1) * block_size); } // Sort each block using a // comparison sort for (let block of blocks) { block.sort(); } // Merge the blocks using an algorithm // similar to merge sort // Initialize the pointers to the // beginning of each block let pointers = new Array(num_blocks).fill(0); let result = []; while ( true ) { // Find the minimum element among // the active blocks let min_val = Infinity; let min_idx = null ; for (let i = 0; i < num_blocks; i++) { if (pointers[i] < blocks[i].length && blocks[i][pointers[i]] < min_val) { min_val = blocks[i][pointers[i]]; min_idx = i; } } // If all blocks are exhausted, // we're done if (min_idx === null ) { break ; } // Otherwise, add the minimum // element to the result and // increment the pointer // for that block result.push(min_val); pointers[min_idx]++; } return result; } // Original Array let arr = [7, 7, 4, 1, 5, 3, 2, 0]; console.log( "Input: " + arr.join( " " )); let result = grail_sort(arr); // Printing result console.log('Output: '+ result.join( " " )); // This code is contributed by Susobhan Akhuli. |
Input : [7, 7, 4, 1, 5, 3, 2, 0] Output: [0, 1, 2, 3, 4, 5, 7, 7]
Time Complexity: O(nlog(n)), where n is the size of the input,
Auxiliary Space: O(1)
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!