Skip to content

refactor: cleanup CountingSort #5275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@
* [CocktailShakerSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/CocktailShakerSort.java)
* [CombSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/CombSort.java)
* [CountingSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/CountingSort.java)
* [CountingSortUsingStream](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/CountingSortUsingStream.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)
* [DualPivotQuickSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/DualPivotQuickSort.java)
Expand All @@ -512,6 +513,8 @@
* [OddEvenSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/OddEvenSort.java)
* [PancakeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/PancakeSort.java)
* [PigeonholeSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/PigeonholeSort.java)
* [PseudoCountingSortUsingTreeMap](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/PseudoCountingSortUsingTreeMap.java)
* [PseudoCountingSortUsingTreeMapAndStream](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/PseudoCountingSortUsingTreeMapAndStream.java)
* [QuickSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/QuickSort.java)
* [RadixSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/RadixSort.java)
* [SelectionSort](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/sorts/SelectionSort.java)
Expand Down Expand Up @@ -864,6 +867,7 @@
* [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)
* [CombSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CombSortTest.java)
* [CountingSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CountingSortTest.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)
* [ExchangeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/ExchangeSortTest.java)
Expand All @@ -875,6 +879,8 @@
* [MergeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/MergeSortTest.java)
* [OddEvenSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/OddEvenSortTest.java)
* [PancakeSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/PancakeSortTest.java)
* [PseudoCountingSortUsingTreeMapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/sorts/PseudoCountingSortUsingTreeMapTest.java)
* [PseudoCountingSortUsingTreeMapAndStreamTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/PseudoCountingSortUsingTreeMapAndStreamTest.java)
* [QuickSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/QuickSortTest.java)
* [SelectionSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SelectionSortTest.java)
* [SelectionSortRecursiveTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/SelectionSortRecursiveTest.java)
Expand Down
107 changes: 34 additions & 73 deletions src/main/java/com/thealgorithms/sorts/CountingSort.java
Original file line number Diff line number Diff line change
@@ -1,93 +1,54 @@
package com.thealgorithms.sorts;

import static com.thealgorithms.sorts.SortUtils.print;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
* @author Youssef Ali (https://github.com/youssefAli11997)
* @author Podshivalov Nikita (https://github.com/nikitap492)
* 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 does not handle negative integers as it
* calculates the range based on the minimum and maximum values of the array.
*
*/
class CountingSort implements SortAlgorithm {

@Override
public <T extends Comparable<T>> T[] sort(T[] unsorted) {
return sort(Arrays.asList(unsorted)).toArray(unsorted);
public final class CountingSort {
private CountingSort() {
}

/**
* This method implements the Generic Counting Sort
* Sorts an array of integers using the Counting Sort algorithm.
*
* @param list The list to be sorted
* <p>
* Sorts the list in increasing order The method uses list elements as keys
* in the frequency map
* @param array the array to be sorted
* @return the sorted array
*/
@Override
public <T extends Comparable<T>> List<T> sort(List<T> list) {
Map<T, Integer> frequency = new TreeMap<>();
// The final output array
List<T> sortedArray = new ArrayList<>(list.size());

// Counting the frequency of @param array elements
list.forEach(v -> frequency.put(v, frequency.getOrDefault(v, 0) + 1));

// Filling the sortedArray
for (Map.Entry<T, Integer> element : frequency.entrySet()) {
for (int j = 0; j < element.getValue(); j++) {
sortedArray.add(element.getKey());
}
public static int[] sort(int[] array) {
if (array.length == 0) {
return array;
}

return sortedArray;
}

/**
* Stream Counting Sort The same as method {@link CountingSort#sort(List)} }
* but this method uses stream API
*
* @param list The list to be sorted
*/
private static <T extends Comparable<T>> List<T> streamSort(List<T> list) {
return list.stream().collect(toMap(k -> k, v -> 1, (v1, v2) -> v1 + v2, TreeMap::new)).entrySet().stream().flatMap(entry -> IntStream.rangeClosed(1, entry.getValue()).mapToObj(t -> entry.getKey())).collect(toList());
}

// Driver Program
public static void main(String[] args) {
// Integer Input
List<Integer> unsortedInts = Stream.of(4, 23, 6, 78, 1, 54, 23, 1, 9, 231, 9, 12).collect(toList());
CountingSort countingSort = new CountingSort();

System.out.println("Before Sorting:");
print(unsortedInts);
int max = Arrays.stream(array).max().orElse(Integer.MIN_VALUE);
int min = Arrays.stream(array).min().orElse(Integer.MAX_VALUE);
int range = max - min + 1;

// Output => 1 1 4 6 9 9 12 23 23 54 78 231
System.out.println("After Sorting:");
print(countingSort.sort(unsortedInts));
System.out.println("After Sorting By Streams:");
print(streamSort(unsortedInts));
int[] count = new int[range];
int[] output = new int[array.length];

System.out.println("\n------------------------------\n");

// String Input
List<String> unsortedStrings = Stream.of("c", "a", "e", "b", "d", "a", "f", "g", "c").collect(toList());
for (int value : array) {
count[value - min]++;
}

System.out.println("Before Sorting:");
print(unsortedStrings);
for (int i = 1; i < count.length; i++) {
count[i] += count[i - 1];
}

// Output => a a b c c d e f g
System.out.println("After Sorting:");
print(countingSort.sort(unsortedStrings));
for (int i = array.length - 1; i >= 0; i--) {
output[count[array[i] - min] - 1] = array[i];
count[array[i] - min]--;
}

System.out.println("After Sorting By Streams:");
print(streamSort(unsortedStrings));
return output;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.thealgorithms.sorts;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
* CountingSort is a generic implementation of the counting sort algorithm.
* This implementation sorts elements that implement the Comparable interface.
*
* @author Youssef Ali (https://github.com/youssefAli11997)
* @author Podshivalov Nikita (https://github.com/nikitap492)
*/
class PseudoCountingSortUsingTreeMap implements SortAlgorithm {
@Override
public <T extends Comparable<T>> T[] sort(T[] array) {
return sort(Arrays.asList(array)).toArray(array);
}

/**
* Sorts the provided list using the counting sort algorithm.
*
* @param list The list to be sorted.
* @param <T> The type of elements in the list, must be Comparable.
* @return A sorted list in increasing order.
*/
@Override
public <T extends Comparable<T>> List<T> sort(List<T> list) {
return extractSortedArray(computeFequencyMap(list));
}

private static <T extends Comparable<T>> List<T> extractSortedArray(final Map<T, Integer> frequencyMap) {
List<T> sortedList = new ArrayList<>();
for (final Map.Entry<T, Integer> entry : frequencyMap.entrySet()) {
for (int i = 0; i < entry.getValue(); i++) {
sortedList.add(entry.getKey());
}
}
return sortedList;
}

private static <T extends Comparable<T>> Map<T, Integer> computeFequencyMap(final List<T> list) {
Map<T, Integer> frequencyMap = new TreeMap<>();
for (final T element : list) {
frequencyMap.put(element, frequencyMap.getOrDefault(element, 0) + 1);
}
return frequencyMap;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.thealgorithms.sorts;

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;

import java.util.Arrays;
import java.util.List;
import java.util.TreeMap;
import java.util.stream.IntStream;

public class PseudoCountingSortUsingTreeMapAndStream implements SortAlgorithm {
@Override
public <T extends Comparable<T>> T[] sort(T[] array) {
return streamSort(Arrays.asList(array)).toArray(array);
}

/**
* Sorts the provided list using the counting sort algorithm with the Stream API.
*
* @param list The list to be sorted.
* @param <T> The type of elements in the list, must be Comparable.
* @return A sorted list in increasing order.
*/
private static <T extends Comparable<T>> List<T> streamSort(List<T> list) {
return list.stream().collect(toMap(k -> k, v -> 1, Integer::sum, TreeMap::new)).entrySet().stream().flatMap(entry -> IntStream.rangeClosed(1, entry.getValue()).mapToObj(t -> entry.getKey())).collect(toList());
}
}
27 changes: 27 additions & 0 deletions src/test/java/com/thealgorithms/sorts/CountingSortTest.java
Original file line number Diff line number Diff line change
@@ -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<TestCase> 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}));
}

@ParameterizedTest
@MethodSource("provideTestCases")
public void testCountingSort(TestCase testCase) {
int[] outputArray = CountingSort.sort(testCase.inputArray);
assertArrayEquals(testCase.expectedArray, outputArray);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.thealgorithms.sorts;

public class PseudoCountingSortUsingTreeMapAndStreamTest extends SortingAlgorithmTest {
@Override
SortAlgorithm getSortAlgorithm() {
return new PseudoCountingSortUsingTreeMapAndStream();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.thealgorithms.sorts;

public class PseudoCountingSortUsingTreeMapTest extends SortingAlgorithmTest {
@Override
SortAlgorithm getSortAlgorithm() {
return new PseudoCountingSortUsingTreeMap();
}
}