From 2d6c39ce100855c87b41f0c3594586ba7fdd6fce Mon Sep 17 00:00:00 2001 From: Alex Klymenko Date: Fri, 12 Jul 2024 20:03:54 +0200 Subject: [PATCH] feat: `CountingSort` implementation (#5287) * feat: CountingSort * checkstyle: fix formatting * refactor: adding additional final modifiers * refactor: restructure sorting, update docs and tests * docs: typo fix --------- Co-authored-by: Alex Klymenko Co-authored-by: Piotr Idzik <65706193+vil02@users.noreply.github.com> --- DIRECTORY.md | 2 + .../com/thealgorithms/sorts/CountingSort.java | 60 +++++++++++++++++++ .../thealgorithms/sorts/CountingSortTest.java | 27 +++++++++ 3 files changed, 89 insertions(+) create mode 100644 src/main/java/com/thealgorithms/sorts/CountingSort.java create mode 100644 src/test/java/com/thealgorithms/sorts/CountingSortTest.java diff --git a/DIRECTORY.md b/DIRECTORY.md index 2b1f4396..aa100502 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -494,6 +494,7 @@ * [BucketSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/BucketSort.java) * [CircleSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/CircleSort.java) * [CocktailShakerSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/CocktailShakerSort.java) + * [CountingSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/CountingSort.java) * [CombSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/CombSort.java) * [CycleSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/CycleSort.java) * [DNFSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/DNFSort.java) @@ -862,6 +863,7 @@ * [BucketSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BucketSortTest.java) * [CircleSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CircleSortTest.java) * [CocktailShakerSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CocktailShakerSortTest.java) + * [CountingSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CountingSortTest.java) * [CombSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CombSortTest.java) * [DualPivotQuickSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/DualPivotQuickSortTest.java) * [DutchNationalFlagSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/DutchNationalFlagSortTest.java) diff --git a/src/main/java/com/thealgorithms/sorts/CountingSort.java b/src/main/java/com/thealgorithms/sorts/CountingSort.java new file mode 100644 index 00000000..5d542050 --- /dev/null +++ b/src/main/java/com/thealgorithms/sorts/CountingSort.java @@ -0,0 +1,60 @@ +package com.thealgorithms.sorts; + +import java.util.Arrays; + +/** + * A standard implementation of the Counting Sort algorithm for integer arrays. + * This implementation has a time complexity of O(n + k), where n is the number + * of elements in the input array and k is the range of the input. + * It works only with integer arrays. + * + * The space complexity is O(k), where k is the range of the input integers. + * + * Note: This implementation handles negative integers as it + * calculates the range based on the minimum and maximum values of the array. + * + */ +public final class CountingSort { + private CountingSort() { + } + + /** + * Sorts an array of integers using the Counting Sort algorithm. + * + * @param array the array to be sorted + * @return the sorted array + */ + public static int[] sort(int[] array) { + if (array.length == 0) { + return array; + } + final var stats = Arrays.stream(array).summaryStatistics(); + final int min = stats.getMin(); + int[] count = computeHistogram(array, min, stats.getMax() - min + 1); + toCumulative(count); + return reconstructSorted(count, min, array); + } + + private static int[] computeHistogram(final int[] array, final int shift, final int spread) { + int[] res = new int[spread]; + for (final var value : array) { + res[value - shift]++; + } + return res; + } + + private static void toCumulative(int[] count) { + for (int i = 1; i < count.length; i++) { + count[i] += count[i - 1]; + } + } + + private static int[] reconstructSorted(final int[] cumulativeCount, final int shift, final int[] array) { + int[] res = new int[array.length]; + for (int i = array.length - 1; i >= 0; i--) { + res[cumulativeCount[array[i] - shift] - 1] = array[i]; + cumulativeCount[array[i] - shift]--; + } + return res; + } +} diff --git a/src/test/java/com/thealgorithms/sorts/CountingSortTest.java b/src/test/java/com/thealgorithms/sorts/CountingSortTest.java new file mode 100644 index 00000000..2426de6f --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/CountingSortTest.java @@ -0,0 +1,27 @@ +package com.thealgorithms.sorts; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class CountingSortTest { + + record TestCase(int[] inputArray, int[] expectedArray) { + } + + static Stream provideTestCases() { + return Stream.of(new TestCase(new int[] {}, new int[] {}), new TestCase(new int[] {4}, new int[] {4}), new TestCase(new int[] {6, 1, 99, 27, 15, 23, 36}, new int[] {1, 6, 15, 23, 27, 36, 99}), new TestCase(new int[] {6, 1, 27, 15, 23, 27, 36, 23}, new int[] {1, 6, 15, 23, 23, 27, 27, 36}), + new TestCase(new int[] {5, 5, 5, 5, 5}, new int[] {5, 5, 5, 5, 5}), new TestCase(new int[] {1, 2, 3, 4, 5}, new int[] {1, 2, 3, 4, 5}), new TestCase(new int[] {5, 4, 3, 2, 1}, new int[] {1, 2, 3, 4, 5}), new TestCase(new int[] {3, -1, 4, 1, 5, -9}, new int[] {-9, -1, 1, 3, 4, 5}), + new TestCase(new int[] {0, 0, 0, 0}, new int[] {0, 0, 0, 0}), new TestCase(new int[] {3, 3, -1, -1, 2, 2, 0, 0}, new int[] {-1, -1, 0, 0, 2, 2, 3, 3}), new TestCase(new int[] {-3, -2, -1, -5, -4}, new int[] {-5, -4, -3, -2, -1}), + new TestCase(new int[] {1000, 500, 100, 50, 10, 5, 1}, new int[] {1, 5, 10, 50, 100, 500, 1000}), new TestCase(new int[] {4, -5, 10, 0}, new int[] {-5, 0, 4, 10})); + } + + @ParameterizedTest + @MethodSource("provideTestCases") + public void testCountingSortException(TestCase testCase) { + int[] outputArray = CountingSort.sort(testCase.inputArray); + assertArrayEquals(testCase.expectedArray, outputArray); + } +}