Skip to content

Commit 7a5fe92

Browse files
authored
feat: FlashSort implementation (#5305)
1 parent 324a35a commit 7a5fe92

File tree

3 files changed

+298
-0
lines changed

3 files changed

+298
-0
lines changed

DIRECTORY.md

+2
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,7 @@
501501
* [DualPivotQuickSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/DualPivotQuickSort.java)
502502
* [DutchNationalFlagSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/DutchNationalFlagSort.java)
503503
* [ExchangeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/ExchangeSort.java)
504+
* [FlashSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/FlashSort.java)
504505
* [GnomeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/GnomeSort.java)
505506
* [HeapSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/HeapSort.java)
506507
* [InsertionSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/InsertionSort.java)
@@ -874,6 +875,7 @@
874875
* [DualPivotQuickSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/DualPivotQuickSortTest.java)
875876
* [DutchNationalFlagSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/DutchNationalFlagSortTest.java)
876877
* [ExchangeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/ExchangeSortTest.java)
878+
* [FlashSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/FlashSortTest.java)
877879
* [GnomeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/GnomeSortTest.java)
878880
* [HeapSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/HeapSortTest.java)
879881
* [InsertionSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/InsertionSortTest.java)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package com.thealgorithms.sorts;
2+
3+
/**
4+
* Implementation of Flash Sort algorithm that implements the SortAlgorithm interface.
5+
*
6+
* Sorts an array using the Flash Sort algorithm.
7+
* <p>
8+
* Flash Sort is a distribution sorting algorithm that partitions the data into
9+
* different classes based on a classification array. It performs the sorting by
10+
* first distributing the data elements into different buckets (or classes) and
11+
* then permuting these buckets into the sorted order.
12+
* <p>
13+
* The method works as follows:
14+
* <ol>
15+
* <li>Finds the minimum and maximum values in the array.</li>
16+
* <li>Initializes a classification array `L` to keep track of the number of elements in each class.</li>
17+
* <li>Computes a normalization constant `c1` to map elements into classes.</li>
18+
* <li>Classifies each element of the array into the corresponding bucket in the classification array.</li>
19+
* <li>Transforms the classification array to compute the starting indices of each bucket.</li>
20+
* <li>Permutes the elements of the array into sorted order based on the classification.</li>
21+
* <li>Uses insertion sort for the final arrangement to ensure complete sorting.</li>
22+
* </ol>
23+
*/
24+
public class FlashSort implements SortAlgorithm {
25+
private double classificationRatio = 0.45;
26+
27+
public FlashSort() {
28+
}
29+
30+
public FlashSort(double classificationRatio) {
31+
if (classificationRatio <= 0 || classificationRatio >= 1) {
32+
throw new IllegalArgumentException("Classification ratio must be between 0 and 1 (exclusive).");
33+
}
34+
this.classificationRatio = classificationRatio;
35+
}
36+
37+
public double getClassificationRatio() {
38+
return classificationRatio;
39+
}
40+
41+
public void setClassificationRatio(double classificationRatio) {
42+
if (classificationRatio <= 0 || classificationRatio >= 1) {
43+
throw new IllegalArgumentException("Classification ratio must be between 0 and 1 (exclusive).");
44+
}
45+
this.classificationRatio = classificationRatio;
46+
}
47+
48+
/**
49+
* Sorts an array using the Flash Sort algorithm.
50+
*
51+
* @param array the array to be sorted.
52+
* @param <T> the type of elements to be sorted, must be comparable.
53+
* @return the sorted array.
54+
*/
55+
@Override
56+
public <T extends Comparable<T>> T[] sort(T[] array) {
57+
flashSort(array);
58+
return array;
59+
}
60+
61+
/**
62+
* Sorts an array using the Flash Sort algorithm.
63+
*
64+
* @param arr the array to be sorted.
65+
* @param <T> the type of elements to be sorted, must be comparable.
66+
*/
67+
private <T extends Comparable<? super T>> void flashSort(T[] arr) {
68+
if (arr.length == 0) {
69+
return;
70+
}
71+
72+
final T min = findMin(arr);
73+
final int maxIndex = findMaxIndex(arr);
74+
75+
if (arr[maxIndex].compareTo(min) == 0) {
76+
return; // All elements are the same
77+
}
78+
79+
final int m = (int) (classificationRatio * arr.length);
80+
81+
final int[] classificationArray = new int[m];
82+
83+
final double c1 = (double) (m - 1) / arr[maxIndex].compareTo(min);
84+
85+
classify(arr, classificationArray, c1, min);
86+
87+
transform(classificationArray);
88+
89+
permute(arr, classificationArray, c1, min, arr.length, m);
90+
91+
insertionSort(arr);
92+
}
93+
94+
/**
95+
* Finds the minimum value in the array.
96+
*
97+
* @param arr the array to find the minimum value in.
98+
* @param <T> the type of elements in the array, must be comparable.
99+
* @return the minimum value in the array.
100+
*/
101+
private <T extends Comparable<? super T>> T findMin(final T[] arr) {
102+
T min = arr[0];
103+
for (int i = 1; i < arr.length; i++) {
104+
if (arr[i].compareTo(min) < 0) {
105+
min = arr[i];
106+
}
107+
}
108+
return min;
109+
}
110+
111+
/**
112+
* Finds the index of the maximum value in the array.
113+
*
114+
* @param arr the array to find the maximum value index in.
115+
* @param <T> the type of elements in the array, must be comparable.
116+
* @return the index of the maximum value in the array.
117+
*/
118+
private <T extends Comparable<? super T>> int findMaxIndex(final T[] arr) {
119+
int maxIndex = 0;
120+
for (int i = 1; i < arr.length; i++) {
121+
if (arr[i].compareTo(arr[maxIndex]) > 0) {
122+
maxIndex = i;
123+
}
124+
}
125+
return maxIndex;
126+
}
127+
128+
/**
129+
* Classifies elements of the array into the classification array classificationArray.
130+
*
131+
* @param arr the array to be classified.
132+
* @param classificationArray the classification array holding the count of elements in each class.
133+
* @param c1 the normalization constant used to map the elements to the classification array.
134+
* @param min the minimum value in the array.
135+
* @param <T> the type of elements in the array, must be comparable.
136+
*/
137+
private <T extends Comparable<? super T>> void classify(final T[] arr, final int[] classificationArray, final double c1, final T min) {
138+
for (int i = 0; i < arr.length; i++) {
139+
int k = (int) (c1 * arr[i].compareTo(min));
140+
classificationArray[k]++;
141+
}
142+
}
143+
144+
/**
145+
* Transforms the classification array classificationArray into the starting index array.
146+
*
147+
* @param classificationArray the classification array holding the count of elements in each class.
148+
*/
149+
private void transform(final int[] classificationArray) {
150+
for (int i = 1; i < classificationArray.length; i++) {
151+
classificationArray[i] += classificationArray[i - 1];
152+
}
153+
}
154+
155+
/**
156+
* Permutes the array into sorted order based on the classification array classificationArray.
157+
*
158+
* @param arr the array to be permuted.
159+
* @param classificationArray the classification array holding the count of elements in each class.
160+
* @param c1 the normalization constant used to map the elements to the classification array.
161+
* @param min the minimum value in the array.
162+
* @param n the length of the array.
163+
* @param m the number of classes in the classification array.
164+
* @param <T> the type of elements in the array, must be comparable.
165+
*/
166+
private <T extends Comparable<? super T>> void permute(final T[] arr, final int[] classificationArray, final double c1, T min, int n, int m) {
167+
int move = 0;
168+
int j = 0;
169+
int k = m - 1;
170+
T flash;
171+
while (move < n - 1) {
172+
while (j > classificationArray[k] - 1) {
173+
j++;
174+
k = (int) (c1 * arr[j].compareTo(min));
175+
}
176+
flash = arr[j];
177+
while (j != classificationArray[k]) {
178+
k = (int) (c1 * flash.compareTo(min));
179+
T temp = arr[classificationArray[k] - 1];
180+
arr[classificationArray[k] - 1] = flash;
181+
flash = temp;
182+
classificationArray[k]--;
183+
move++;
184+
}
185+
}
186+
}
187+
188+
/**
189+
* Sorts an array using the insertion sort algorithm.
190+
*
191+
* @param arr the array to be sorted.
192+
* @param <T> the type of elements to be sorted, must be comparable.
193+
*/
194+
private <T extends Comparable<? super T>> void insertionSort(final T[] arr) {
195+
int n = arr.length;
196+
for (int i = 1; i < n; i++) {
197+
T key = arr[i];
198+
int j = i - 1;
199+
while (j >= 0 && arr[j].compareTo(key) > 0) {
200+
arr[j + 1] = arr[j];
201+
j--;
202+
}
203+
arr[j + 1] = key;
204+
}
205+
}
206+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.thealgorithms.sorts;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
6+
import java.lang.reflect.Method;
7+
import java.util.ArrayList;
8+
import java.util.Collection;
9+
import java.util.List;
10+
import org.junit.jupiter.api.DynamicTest;
11+
import org.junit.jupiter.api.Test;
12+
import org.junit.jupiter.api.TestFactory;
13+
import org.junit.jupiter.api.function.Executable;
14+
import org.junit.jupiter.params.ParameterizedTest;
15+
import org.junit.jupiter.params.provider.ValueSource;
16+
17+
public class FlashSortTest extends SortingAlgorithmTest {
18+
private final FlashSort flashSort = new FlashSort();
19+
20+
public FlashSort getFlashSort() {
21+
return flashSort;
22+
}
23+
24+
@Override
25+
SortAlgorithm getSortAlgorithm() {
26+
return getFlashSort();
27+
}
28+
29+
@Test
30+
public void testDefaultConstructor() {
31+
double defaultRation = 0.45;
32+
FlashSort sorter = new FlashSort();
33+
assertEquals(defaultRation, sorter.getClassificationRatio());
34+
}
35+
36+
@ParameterizedTest
37+
@ValueSource(doubles = {0.1, 0.2, 0.5, 0.9})
38+
public void testCustomConstructorValidRatio(double ratio) {
39+
FlashSort sorter = new FlashSort(ratio);
40+
assertEquals(ratio, sorter.getClassificationRatio());
41+
}
42+
43+
@ParameterizedTest
44+
@ValueSource(doubles = {0, 1, -0.1, 1.1})
45+
public void testCustomConstructorInvalidRatio(double ratio) {
46+
assertThrows(IllegalArgumentException.class, () -> new FlashSort(ratio));
47+
}
48+
49+
@TestFactory
50+
public Collection<DynamicTest> dynamicTestsForSorting() {
51+
List<DynamicTest> dynamicTests = new ArrayList<>();
52+
double[] ratios = {0.1, 0.2, 0.5, 0.9};
53+
54+
for (double ratio : ratios) {
55+
FlashSort sorter = (FlashSort) getSortAlgorithm();
56+
sorter.setClassificationRatio(ratio);
57+
dynamicTests.addAll(createDynamicTestsForRatio(ratio));
58+
}
59+
60+
return dynamicTests;
61+
}
62+
63+
private Collection<DynamicTest> createDynamicTestsForRatio(double ratio) {
64+
List<DynamicTest> dynamicTests = new ArrayList<>();
65+
for (TestMethod testMethod : getTestMethodsFromSuperClass()) {
66+
dynamicTests.add(DynamicTest.dynamicTest("Ratio: " + ratio + " - Test: " + testMethod.name(), testMethod.executable()));
67+
}
68+
return dynamicTests;
69+
}
70+
71+
private List<TestMethod> getTestMethodsFromSuperClass() {
72+
List<TestMethod> testMethods = new ArrayList<>();
73+
Method[] methods = SortingAlgorithmTest.class.getDeclaredMethods();
74+
for (Method method : methods) {
75+
if (method.isAnnotationPresent(Test.class)) {
76+
testMethods.add(new TestMethod(() -> {
77+
try {
78+
method.invoke(this);
79+
} catch (Exception e) {
80+
throw new RuntimeException(e);
81+
}
82+
}, method.getName()));
83+
}
84+
}
85+
return testMethods;
86+
}
87+
88+
record TestMethod(Executable executable, String name) {
89+
}
90+
}

0 commit comments

Comments
 (0)