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:
- Rotate the remaining blocks:
- Merge adjacent blocks:
- Divide the array into blocks of size 2:
- Merge adjacent blocks:
- Divide the array into blocks of size 4:
- (8) Merge adjacent blocks:
Below is the implementation for the above approach:
C++
#include <algorithm>
#include <cmath>
#include <iostream>
#include <limits>
#include <vector>
using namespace std;
vector< int > grailSort(vector< int > arr)
{
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());
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;
}
}
vector< int > pointers(numBlocks);
vector< int > result;
while ( true ) {
int minVal = numeric_limits< int >::max();
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 (minIdx == -1) {
break ;
}
result.push_back(minVal);
pointers[minIdx]++;
}
return result;
}
int main()
{
vector< int > arr = { 7, 7, 4, 1, 5, 3, 2, 0 };
cout << "Input : " ;
for ( auto x : arr) {
cout << x << " " ;
}
cout << endl;
vector< int > result = grailSort(arr);
cout << "Output: " ;
for ( auto x : result) {
cout << x << " " ;
}
cout << endl;
return 0;
}
|
Java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class GrailSort {
public static List<Integer> grailSort(List<Integer> arr)
{
int blockSize = ( int )Math.sqrt(arr.size());
int numBlocks
= (arr.size() + blockSize - 1 ) / blockSize;
List<List<Integer> > blocks = new ArrayList<>(
numBlocks);
for ( int i = 0 ; i < numBlocks; i++) {
blocks.add( new ArrayList<>(Collections.nCopies(
blockSize,
0 )));
Collections.copy(
blocks.get(i),
arr.subList(i * blockSize,
Math.min((i + 1 ) * blockSize,
arr.size())));
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);
}
}
List<Integer> pointers
= new ArrayList<>(Collections.nCopies(
numBlocks,
0 ));
List<Integer> result
= new ArrayList<>();
while ( true ) {
int minVal
= Integer.MAX_VALUE;
int minIdx = - 1 ;
for ( int i = 0 ; i < numBlocks; i++) {
if (pointers.get(i)
< blocks.get(i)
.size()
&& blocks.get(i).get(pointers.get(i))
< minVal) {
minVal = blocks.get(i).get(
pointers.get(i));
minIdx = i;
}
}
if (minIdx == - 1 ) {
break ;
}
result.add(minVal);
pointers.set(minIdx, pointers.get(minIdx) + 1 );
}
return result;
}
public static void main(String[] args)
{
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();
List<Integer> result = grailSort(arr);
System.out.print( "Output: " );
for ( int x : result)
System.out.print(x + " " );
}
}
|
Python3
def grail_sort(arr):
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)]
for block in blocks:
block.sort()
pointers = [ 0 ] * num_blocks
result = []
while True :
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 min_idx is None :
break
result.append(min_val)
pointers[min_idx] + = 1
return result
arr = [ 7 , 7 , 4 , 1 , 5 , 3 , 2 , 0 ]
print ( 'Input : ' , arr)
print ( "Output: " , grail_sort(arr))
|
C#
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static List< int > GrailSort(List< int > arr)
{
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));
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;
}
}
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 ) {
int minVal = int .MaxValue;
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 (minIdx == -1) {
break ;
}
result.Add(minVal);
pointers[minIdx]++;
}
return result;
}
static void Main( string [] args)
{
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();
List< int > result = GrailSort(arr);
Console.Write( "Output: " );
foreach ( int x in result) { Console.Write(x + " " ); }
Console.WriteLine();
}
}
|
Javascript
function grail_sort(arr)
{
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);
}
for (let block of blocks) {
block.sort();
}
let pointers = new Array(num_blocks).fill(0);
let result = [];
while ( true )
{
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 (min_idx === null ) {
break ;
}
result.push(min_val);
pointers[min_idx]++;
}
return result;
}
let arr = [7, 7, 4, 1, 5, 3, 2, 0];
console.log( "Input: " + arr.join( " " ));
let result = grail_sort(arr);
console.log('Output: '+ result.join( " " ));
|
Output
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)
|