Skip to content

Commit 6e23e19

Browse files
authored
feat: SpreadSort implementation (#5308)
1 parent 357e15a commit 6e23e19

File tree

3 files changed

+312
-0
lines changed

3 files changed

+312
-0
lines changed

DIRECTORY.md

+2
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@
523523
* [SortAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SortAlgorithm.java)
524524
* [SortUtils](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SortUtils.java)
525525
* [SortUtilsRandomGenerator](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SortUtilsRandomGenerator.java)
526+
* [SpreadSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SpreadSort.java)
526527
* [StoogeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/StoogeSort.java)
527528
* [StrandSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/StrandSort.java)
528529
* [SwapSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SwapSort.java)
@@ -893,6 +894,7 @@
893894
* [SortingAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SortingAlgorithmTest.java)
894895
* [SortUtilsRandomGeneratorTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SortUtilsRandomGeneratorTest.java)
895896
* [SortUtilsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SortUtilsTest.java)
897+
* [SpreadSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SpreadSortTest.java)
896898
* [StoogeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/StoogeSortTest.java)
897899
* [StrandSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/StrandSortTest.java)
898900
* [SwapSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SwapSortTest.java)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
package com.thealgorithms.sorts;
2+
import java.util.Arrays;
3+
4+
/**
5+
* SpreadSort is a highly efficient sorting algorithm suitable for large datasets.
6+
* It distributes elements into buckets and recursively sorts these buckets.
7+
* This implementation is generic and can sort any array of elements that extend Comparable.
8+
*/
9+
public class SpreadSort implements SortAlgorithm {
10+
private static final int MAX_INSERTION_SORT_THRESHOLD = 1000;
11+
private static final int MAX_INITIAL_BUCKET_CAPACITY = 1000;
12+
private static final int MAX_MIN_BUCKETS = 100;
13+
14+
private final int insertionSortThreshold;
15+
private final int initialBucketCapacity;
16+
private final int minBuckets;
17+
18+
/**
19+
* Constructor to initialize the SpreadSort algorithm with custom parameters.
20+
*
21+
* @param insertionSortThreshold the threshold for using insertion sort for small segments (1-1000)
22+
* @param initialBucketCapacity the initial capacity for each bucket (1-1000)
23+
* @param minBuckets the minimum number of buckets to use (1-100)
24+
*/
25+
public SpreadSort(int insertionSortThreshold, int initialBucketCapacity, int minBuckets) {
26+
if (insertionSortThreshold < 1 || insertionSortThreshold > MAX_INSERTION_SORT_THRESHOLD) {
27+
throw new IllegalArgumentException("Insertion sort threshold must be between 1 and " + MAX_INSERTION_SORT_THRESHOLD);
28+
}
29+
if (initialBucketCapacity < 1 || initialBucketCapacity > MAX_INITIAL_BUCKET_CAPACITY) {
30+
throw new IllegalArgumentException("Initial bucket capacity must be between 1 and " + MAX_INITIAL_BUCKET_CAPACITY);
31+
}
32+
if (minBuckets < 1 || minBuckets > MAX_MIN_BUCKETS) {
33+
throw new IllegalArgumentException("Minimum number of buckets must be between 1 and " + MAX_MIN_BUCKETS);
34+
}
35+
36+
this.insertionSortThreshold = insertionSortThreshold;
37+
this.initialBucketCapacity = initialBucketCapacity;
38+
this.minBuckets = minBuckets;
39+
}
40+
41+
/**
42+
* Default constructor with predefined values.
43+
*/
44+
public SpreadSort() {
45+
this(16, 16, 2);
46+
}
47+
48+
/**
49+
* Sorts an array using the SpreadSort algorithm.
50+
*
51+
* @param array the array to be sorted
52+
* @param <T> the type of elements in the array
53+
* @return the sorted array
54+
*/
55+
@Override
56+
public <T extends Comparable<T>> T[] sort(T[] array) {
57+
if (array.length == 0) {
58+
return array;
59+
}
60+
spreadSort(array, 0, array.length - 1);
61+
return array;
62+
}
63+
64+
/**
65+
* Internal method to sort an array segment using the SpreadSort algorithm.
66+
*
67+
* @param array the array to be sorted
68+
* @param left the left boundary of the segment
69+
* @param right the right boundary of the segment
70+
* @param <T> the type of elements in the array
71+
*/
72+
private <T extends Comparable<T>> void spreadSort(final T[] array, final int left, final int right) {
73+
if (left >= right) {
74+
return;
75+
}
76+
77+
// Base case for small segments
78+
if (right - left < insertionSortThreshold) {
79+
insertionSort(array, left, right);
80+
return;
81+
}
82+
83+
T min = findMin(array, left, right);
84+
T max = findMax(array, left, right);
85+
86+
if (min.equals(max)) {
87+
return; // All elements are the same
88+
}
89+
90+
int numBuckets = calculateNumBuckets(right - left + 1);
91+
final Bucket<T>[] buckets = createBuckets(numBuckets);
92+
93+
distributeElements(array, left, right, min, max, numBuckets, buckets);
94+
collectElements(array, left, buckets);
95+
}
96+
97+
/**
98+
* Finds the minimum element in the specified segment of the array.
99+
*
100+
* @param array the array to search
101+
* @param left the left boundary of the segment
102+
* @param right the right boundary of the segment
103+
* @param <T> the type of elements in the array
104+
* @return the minimum element
105+
*/
106+
private <T extends Comparable<T>> T findMin(final T[] array, final int left, final int right) {
107+
T min = array[left];
108+
for (int i = left + 1; i <= right; i++) {
109+
if (SortUtils.less(array[i], min)) {
110+
min = array[i];
111+
}
112+
}
113+
return min;
114+
}
115+
116+
/**
117+
* Finds the maximum element in the specified segment of the array.
118+
*
119+
* @param array the array to search
120+
* @param left the left boundary of the segment
121+
* @param right the right boundary of the segment
122+
* @param <T> the type of elements in the array
123+
* @return the maximum element
124+
*/
125+
private <T extends Comparable<T>> T findMax(final T[] array, final int left, final int right) {
126+
T max = array[left];
127+
for (int i = left + 1; i <= right; i++) {
128+
if (SortUtils.greater(array[i], max)) {
129+
max = array[i];
130+
}
131+
}
132+
return max;
133+
}
134+
135+
/**
136+
* Calculates the number of buckets needed based on the size of the segment.
137+
*
138+
* @param segmentSize the size of the segment
139+
* @return the number of buckets
140+
*/
141+
private int calculateNumBuckets(final int segmentSize) {
142+
int numBuckets = segmentSize / insertionSortThreshold;
143+
return Math.max(numBuckets, minBuckets);
144+
}
145+
146+
/**
147+
* Creates an array of buckets.
148+
*
149+
* @param numBuckets the number of buckets to create
150+
* @param <T> the type of elements in the buckets
151+
* @return an array of buckets
152+
*/
153+
@SuppressWarnings("unchecked")
154+
private <T extends Comparable<T>> Bucket<T>[] createBuckets(final int numBuckets) {
155+
final Bucket<T>[] buckets = new Bucket[numBuckets];
156+
for (int i = 0; i < numBuckets; i++) {
157+
buckets[i] = new Bucket<>(initialBucketCapacity);
158+
}
159+
return buckets;
160+
}
161+
162+
/**
163+
* Distributes elements of the array segment into buckets.
164+
*
165+
* @param array the array to be sorted
166+
* @param left the left boundary of the segment
167+
* @param right the right boundary of the segment
168+
* @param min the minimum element in the segment
169+
* @param max the maximum element in the segment
170+
* @param numBuckets the number of buckets
171+
* @param buckets the array of buckets
172+
* @param <T> the type of elements in the array
173+
*/
174+
private <T extends Comparable<T>> void distributeElements(final T[] array, final int left, final int right, final T min, final T max, final int numBuckets, final Bucket<T>[] buckets) {
175+
final double range = max.compareTo(min);
176+
for (int i = left; i <= right; i++) {
177+
final int scaleRangeDifference = array[i].compareTo(min) * numBuckets;
178+
int bucketIndex = (int) (scaleRangeDifference / (range + 1));
179+
buckets[bucketIndex].add(array[i]);
180+
}
181+
}
182+
183+
/**
184+
* Collects elements from the buckets back into the array.
185+
*
186+
* @param array the array to be sorted
187+
* @param left the left boundary of the segment
188+
* @param buckets the array of buckets
189+
* @param <T> the type of elements in the array
190+
*/
191+
private <T extends Comparable<T>> void collectElements(final T[] array, final int left, final Bucket<T>[] buckets) {
192+
int index = left;
193+
for (Bucket<T> bucket : buckets) {
194+
if (bucket.size() > 0) {
195+
T[] bucketArray = bucket.toArray();
196+
spreadSort(bucketArray, 0, bucketArray.length - 1);
197+
for (T element : bucketArray) {
198+
array[index++] = element;
199+
}
200+
}
201+
}
202+
}
203+
204+
/**
205+
* Insertion sort implementation for small segments.
206+
*
207+
* @param array the array to be sorted
208+
* @param left the left boundary of the segment
209+
* @param right the right boundary of the segment
210+
* @param <T> the type of elements in the array
211+
*/
212+
private <T extends Comparable<T>> void insertionSort(final T[] array, final int left, final int right) {
213+
for (int i = left + 1; i <= right; i++) {
214+
T key = array[i];
215+
int j = i - 1;
216+
while (j >= left && SortUtils.greater(array[j], key)) {
217+
array[j + 1] = array[j];
218+
j--;
219+
}
220+
array[j + 1] = key;
221+
}
222+
}
223+
224+
/**
225+
* Bucket class to hold elements during sorting.
226+
*
227+
* @param <T> the type of elements in the bucket
228+
*/
229+
private static class Bucket<T extends Comparable<T>> {
230+
private T[] elements;
231+
private int size;
232+
233+
/**
234+
* Constructs a new bucket with initial capacity.
235+
*/
236+
@SuppressWarnings("unchecked")
237+
Bucket(int initialBucketCapacity) {
238+
elements = (T[]) new Comparable[initialBucketCapacity];
239+
size = 0;
240+
}
241+
242+
/**
243+
* Adds an element to the bucket.
244+
*
245+
* @param element the element to add
246+
*/
247+
void add(T element) {
248+
if (size == elements.length) {
249+
elements = Arrays.copyOf(elements, size * 2);
250+
}
251+
elements[size++] = element;
252+
}
253+
254+
/**
255+
* Returns the number of elements in the bucket.
256+
*
257+
* @return the size of the bucket
258+
*/
259+
int size() {
260+
return size;
261+
}
262+
263+
/**
264+
* Returns an array containing all elements in the bucket.
265+
*
266+
* @return an array containing all elements in the bucket
267+
*/
268+
@SuppressWarnings("unchecked")
269+
T[] toArray() {
270+
return Arrays.copyOf(elements, size);
271+
}
272+
}
273+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.thealgorithms.sorts;
2+
3+
import static org.junit.jupiter.api.Assertions.assertThrows;
4+
5+
import java.util.stream.Stream;
6+
import org.junit.jupiter.api.function.Executable;
7+
import org.junit.jupiter.params.ParameterizedTest;
8+
import org.junit.jupiter.params.provider.Arguments;
9+
import org.junit.jupiter.params.provider.ArgumentsProvider;
10+
import org.junit.jupiter.params.provider.ArgumentsSource;
11+
12+
public class SpreadSortTest extends SortingAlgorithmTest {
13+
14+
protected int getGeneratedArraySize() {
15+
return 1000;
16+
}
17+
18+
@Override
19+
SortAlgorithm getSortAlgorithm() {
20+
return new SpreadSort();
21+
}
22+
23+
static class ConstructorArgumentsProvider implements ArgumentsProvider {
24+
@Override
25+
public Stream<? extends Arguments> provideArguments(org.junit.jupiter.api.extension.ExtensionContext context) {
26+
return Stream.of(Arguments.of(0, 16, 2, IllegalArgumentException.class), Arguments.of(16, 0, 2, IllegalArgumentException.class), Arguments.of(16, 16, 0, IllegalArgumentException.class), Arguments.of(1001, 16, 2, IllegalArgumentException.class),
27+
Arguments.of(16, 1001, 2, IllegalArgumentException.class), Arguments.of(16, 16, 101, IllegalArgumentException.class));
28+
}
29+
}
30+
31+
@ParameterizedTest
32+
@ArgumentsSource(ConstructorArgumentsProvider.class)
33+
void testConstructor(int insertionSortThreshold, int initialBucketCapacity, int minBuckets, Class<Exception> expectedException) {
34+
Executable executable = () -> new SpreadSort(insertionSortThreshold, initialBucketCapacity, minBuckets);
35+
assertThrows(expectedException, executable);
36+
}
37+
}

0 commit comments

Comments
 (0)