From 0cd0ef2574596ab9b733451986b3c270b9b84da5 Mon Sep 17 00:00:00 2001 From: Alex Klymenko Date: Sat, 10 Aug 2024 00:31:26 +0200 Subject: [PATCH 1/3] refactor: refactoring BucketSort implementation, adding more tests --- .../com/thealgorithms/sorts/BucketSort.java | 136 ++++++++---------- .../thealgorithms/sorts/BucketSortTest.java | 70 +++++++-- 2 files changed, 114 insertions(+), 92 deletions(-) diff --git a/src/main/java/com/thealgorithms/sorts/BucketSort.java b/src/main/java/com/thealgorithms/sorts/BucketSort.java index 2a48cca0f433..4256f51d1c1f 100644 --- a/src/main/java/com/thealgorithms/sorts/BucketSort.java +++ b/src/main/java/com/thealgorithms/sorts/BucketSort.java @@ -1,119 +1,101 @@ package com.thealgorithms.sorts; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Random; +import java.util.stream.Collectors; +import java.util.stream.IntStream; /** - * Wikipedia: https://en.wikipedia.org/wiki/Bucket_sort + * BucketSort class provides a static method to sort an array of integers using the Bucket Sort algorithm. */ public final class BucketSort { private BucketSort() { } - public static void main(String[] args) { - int[] arr = new int[10]; - - /* generate 10 random numbers from -50 to 49 */ - Random random = new Random(); - for (int i = 0; i < arr.length; ++i) { - arr[i] = random.nextInt(100) - 50; - } - - bucketSort(arr); - - /* check array is sorted or not */ - for (int i = 0, limit = arr.length - 1; i < limit; ++i) { - assert arr[i] <= arr[i + 1]; - } - } - /** - * BucketSort algorithms implements + * Sorts the given array using the Bucket Sort algorithm. * - * @param arr the array contains elements + * @param arr the array to be sorted + * @return the sorted array */ public static int[] bucketSort(int[] arr) { - /* get max value of arr */ - int max = max(arr); - - /* get min value of arr */ - int min = min(arr); + if (arr.length == 0) { + return arr; + } - /* number of buckets */ + int min = Arrays.stream(arr).min().getAsInt(); + int max = Arrays.stream(arr).max().getAsInt(); int numberOfBuckets = max - min + 1; - List> buckets = new ArrayList<>(numberOfBuckets); + List> buckets = initializeBuckets(numberOfBuckets); - /* init buckets */ - for (int i = 0; i < numberOfBuckets; ++i) { - buckets.add(new ArrayList<>()); - } - - /* store elements to buckets */ - for (int value : arr) { - int hash = hash(value, min, numberOfBuckets); - buckets.get(hash).add(value); - } + distributeElementsToBuckets(arr, buckets, min, numberOfBuckets); + sortBuckets(buckets); - /* sort individual bucket */ - for (List bucket : buckets) { - Collections.sort(bucket); - } + return concatenateBuckets(buckets, arr); + } - /* concatenate buckets to origin array */ - int index = 0; - for (List bucket : buckets) { - for (int value : bucket) { - arr[index++] = value; - } - } + /** + * Initializes the buckets for sorting. + * + * @param numberOfBuckets the number of buckets to initialize + * @return a list of empty buckets + */ + private static List> initializeBuckets(final int numberOfBuckets) { + return IntStream.range(0, numberOfBuckets) + .mapToObj(i -> new ArrayList()) + .collect(Collectors.toList()); + } - return arr; + /** + * Distributes the elements of the array into the appropriate buckets. + * + * @param arr the array to distribute + * @param buckets the list of buckets + * @param min the minimum value in the array + * @param numberOfBuckets the total number of buckets + */ + private static void distributeElementsToBuckets(int[] arr, List> buckets, final int min, final int numberOfBuckets) { + Arrays.stream(arr).forEach(value -> buckets.get(hash(value, min, numberOfBuckets)).add(value)); } /** - * Get index of bucket which of our elements gets placed into it. + * Sorts each bucket individually. * - * @param elem the element of array to be sorted - * @param min min value of array - * @param numberOfBucket the number of bucket - * @return index of bucket + * @param buckets the list of buckets to sort */ - private static int hash(int elem, int min, int numberOfBucket) { - return (elem - min) / numberOfBucket; + private static void sortBuckets(List> buckets) { + buckets.forEach(Collections::sort); } /** - * Calculate max value of array + * Concatenates the sorted buckets into the original array. * - * @param arr the array contains elements - * @return max value of given array + * @param buckets the list of sorted buckets + * @param arr the original array + * @return the sorted array */ - public static int max(int[] arr) { - int max = arr[0]; - for (int value : arr) { - if (value > max) { - max = value; + private static int[] concatenateBuckets(List> buckets, int[] arr) { + int index = 0; + for (List bucket : buckets) { + for (int value : bucket) { + arr[index++] = value; } } - return max; + return arr; } /** - * Calculate min value of array + * Computes the hash value to determine which bucket an element should be placed in. * - * @param arr the array contains elements - * @return min value of given array + * @param element the element of the array + * @param min the minimum value in the array + * @param numberOfBuckets the total number of buckets + * @return the index of the bucket */ - public static int min(int[] arr) { - int min = arr[0]; - for (int value : arr) { - if (value < min) { - min = value; - } - } - return min; + private static int hash(final int element, final int min, final int numberOfBuckets) { + return (element - min) / numberOfBuckets; } } diff --git a/src/test/java/com/thealgorithms/sorts/BucketSortTest.java b/src/test/java/com/thealgorithms/sorts/BucketSortTest.java index bd9d2e3d60cf..03e0870a4eda 100644 --- a/src/test/java/com/thealgorithms/sorts/BucketSortTest.java +++ b/src/test/java/com/thealgorithms/sorts/BucketSortTest.java @@ -7,42 +7,82 @@ public class BucketSortTest { @Test - public void bucketSortSingleIntegerArray() { + public void sortSingleIntegerArray() { int[] inputArray = {4}; - int[] outputArray = BucketSort.bucketSort(inputArray); int[] expectedOutput = {4}; - assertArrayEquals(outputArray, expectedOutput); + int[] outputArray = BucketSort.bucketSort(inputArray); + assertArrayEquals(expectedOutput, outputArray); } @Test - public void bucketSortNonDuplicateIntegerArray() { - int[] inputArray = {6, 1, 99, 27, 15, 23, 36}; + public void sortEmptyArray() { + int[] inputArray = {}; + int[] expectedOutput = {}; + int[] outputArray = BucketSort.bucketSort(inputArray); + assertArrayEquals(expectedOutput, outputArray); + } + + @Test + public void sortAlreadySortedArray() { + int[] inputArray = {1, 2, 3, 4, 5, 6, 7}; + int[] expectedOutput = {1, 2, 3, 4, 5, 6, 7}; int[] outputArray = BucketSort.bucketSort(inputArray); + assertArrayEquals(expectedOutput, outputArray); + } + + @Test + public void sortNonDuplicateIntegerArray() { + int[] inputArray = {6, 1, 99, 27, 15, 23, 36}; int[] expectedOutput = {1, 6, 15, 23, 27, 36, 99}; - assertArrayEquals(outputArray, expectedOutput); + int[] outputArray = BucketSort.bucketSort(inputArray); + assertArrayEquals(expectedOutput, outputArray); } @Test - public void bucketSortDuplicateIntegerArray() { + public void sortDuplicateIntegerArray() { int[] inputArray = {6, 1, 27, 15, 23, 27, 36, 23}; - int[] outputArray = BucketSort.bucketSort(inputArray); int[] expectedOutput = {1, 6, 15, 23, 23, 27, 27, 36}; - assertArrayEquals(outputArray, expectedOutput); + int[] outputArray = BucketSort.bucketSort(inputArray); + assertArrayEquals(expectedOutput, outputArray); } @Test - public void bucketSortNonDuplicateIntegerArrayWithNegativeNum() { + public void sortNonDuplicateIntegerArrayWithNegativeNum() { int[] inputArray = {6, -1, 99, 27, -15, 23, -36}; - int[] outputArray = BucketSort.bucketSort(inputArray); int[] expectedOutput = {-36, -15, -1, 6, 23, 27, 99}; - assertArrayEquals(outputArray, expectedOutput); + int[] outputArray = BucketSort.bucketSort(inputArray); + assertArrayEquals(expectedOutput, outputArray); } @Test - public void bucketSortDuplicateIntegerArrayWithNegativeNum() { + public void sortDuplicateIntegerArrayWithNegativeNum() { int[] inputArray = {6, -1, 27, -15, 23, 27, -36, 23}; - int[] outputArray = BucketSort.bucketSort(inputArray); int[] expectedOutput = {-36, -15, -1, 6, 23, 23, 27, 27}; - assertArrayEquals(outputArray, expectedOutput); + int[] outputArray = BucketSort.bucketSort(inputArray); + assertArrayEquals(expectedOutput, outputArray); + } + + @Test + public void sortLargeArray() { + int[] inputArray = {100, 50, -100, 200, 0, 150, -50}; + int[] expectedOutput = {-100, -50, 0, 50, 100, 150, 200}; + int[] outputArray = BucketSort.bucketSort(inputArray); + assertArrayEquals(expectedOutput, outputArray); + } + + @Test + public void sortArrayWithAllIdenticalElements() { + int[] inputArray = {5, 5, 5, 5, 5}; + int[] expectedOutput = {5, 5, 5, 5, 5}; + int[] outputArray = BucketSort.bucketSort(inputArray); + assertArrayEquals(expectedOutput, outputArray); + } + + @Test + public void sortArrayWithMixedPositiveAndNegativeNumbers() { + int[] inputArray = {-3, 0, 2, -2, 3, 1, -1}; + int[] expectedOutput = {-3, -2, -1, 0, 1, 2, 3}; + int[] outputArray = BucketSort.bucketSort(inputArray); + assertArrayEquals(expectedOutput, outputArray); } } From aae59ba871a412945a767ab6d02782a94f8e0963 Mon Sep 17 00:00:00 2001 From: Alex Klymenko Date: Sat, 10 Aug 2024 10:15:34 +0200 Subject: [PATCH 2/3] checkstyle: fix stream formatting --- src/main/java/com/thealgorithms/sorts/BucketSort.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/com/thealgorithms/sorts/BucketSort.java b/src/main/java/com/thealgorithms/sorts/BucketSort.java index 4256f51d1c1f..a70bbdce37bc 100644 --- a/src/main/java/com/thealgorithms/sorts/BucketSort.java +++ b/src/main/java/com/thealgorithms/sorts/BucketSort.java @@ -44,9 +44,7 @@ public static int[] bucketSort(int[] arr) { * @return a list of empty buckets */ private static List> initializeBuckets(final int numberOfBuckets) { - return IntStream.range(0, numberOfBuckets) - .mapToObj(i -> new ArrayList()) - .collect(Collectors.toList()); + return IntStream.range(0, numberOfBuckets).mapToObj(i -> new ArrayList()).collect(Collectors.toList()); } /** From 08d4d5dc9596fdd21d218fb2aa590b231cb85914 Mon Sep 17 00:00:00 2001 From: Alex Klymenko Date: Sat, 10 Aug 2024 11:23:34 +0200 Subject: [PATCH 3/3] refactor: refactoring implementation to common sort approach --- .../com/thealgorithms/sorts/BucketSort.java | 129 +++++++++++------- .../thealgorithms/sorts/BucketSortTest.java | 88 +----------- 2 files changed, 84 insertions(+), 133 deletions(-) diff --git a/src/main/java/com/thealgorithms/sorts/BucketSort.java b/src/main/java/com/thealgorithms/sorts/BucketSort.java index a70bbdce37bc..a6901ac339ac 100644 --- a/src/main/java/com/thealgorithms/sorts/BucketSort.java +++ b/src/main/java/com/thealgorithms/sorts/BucketSort.java @@ -1,99 +1,130 @@ package com.thealgorithms.sorts; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; /** - * BucketSort class provides a static method to sort an array of integers using the Bucket Sort algorithm. + * BucketSort class provides a method to sort an array of elements using the Bucket Sort algorithm + * and implements the SortAlgorithm interface. */ -public final class BucketSort { - private BucketSort() { - } +public class BucketSort implements SortAlgorithm { - /** - * Sorts the given array using the Bucket Sort algorithm. - * - * @param arr the array to be sorted - * @return the sorted array - */ - public static int[] bucketSort(int[] arr) { - if (arr.length == 0) { - return arr; + // Constant that defines the divisor for determining the number of buckets + private static final int BUCKET_DIVISOR = 10; + + @Override + public > T[] sort(T[] array) { + if (array.length == 0) { + return array; } - int min = Arrays.stream(arr).min().getAsInt(); - int max = Arrays.stream(arr).max().getAsInt(); - int numberOfBuckets = max - min + 1; + T min = findMin(array); + T max = findMax(array); + int numberOfBuckets = calculateNumberOfBuckets(array.length); - List> buckets = initializeBuckets(numberOfBuckets); + List> buckets = initializeBuckets(numberOfBuckets); + distributeElementsIntoBuckets(array, buckets, min, max, numberOfBuckets); - distributeElementsToBuckets(arr, buckets, min, numberOfBuckets); - sortBuckets(buckets); + return concatenateBuckets(buckets, array); + } - return concatenateBuckets(buckets, arr); + /** + * Calculates the number of buckets to use based on the size of the array. + * + * @param arrayLength the length of the array + * @return the number of buckets + */ + private int calculateNumberOfBuckets(final int arrayLength) { + return Math.max(arrayLength / BUCKET_DIVISOR, 1); } /** - * Initializes the buckets for sorting. + * Initializes a list of empty buckets. * * @param numberOfBuckets the number of buckets to initialize + * @param the type of elements to be sorted * @return a list of empty buckets */ - private static List> initializeBuckets(final int numberOfBuckets) { - return IntStream.range(0, numberOfBuckets).mapToObj(i -> new ArrayList()).collect(Collectors.toList()); + private > List> initializeBuckets(int numberOfBuckets) { + List> buckets = new ArrayList<>(numberOfBuckets); + for (int i = 0; i < numberOfBuckets; i++) { + buckets.add(new ArrayList<>()); + } + return buckets; } /** - * Distributes the elements of the array into the appropriate buckets. + * Distributes elements from the array into the appropriate buckets. * - * @param arr the array to distribute + * @param array the array of elements to distribute * @param buckets the list of buckets * @param min the minimum value in the array + * @param max the maximum value in the array * @param numberOfBuckets the total number of buckets + * @param the type of elements in the array */ - private static void distributeElementsToBuckets(int[] arr, List> buckets, final int min, final int numberOfBuckets) { - Arrays.stream(arr).forEach(value -> buckets.get(hash(value, min, numberOfBuckets)).add(value)); - } - - /** - * Sorts each bucket individually. - * - * @param buckets the list of buckets to sort - */ - private static void sortBuckets(List> buckets) { - buckets.forEach(Collections::sort); + private > void distributeElementsIntoBuckets(T[] array, List> buckets, final T min, final T max, final int numberOfBuckets) { + for (final T element : array) { + int bucketIndex = hash(element, min, max, numberOfBuckets); + buckets.get(bucketIndex).add(element); + } } /** - * Concatenates the sorted buckets into the original array. + * Concatenates the sorted buckets back into the original array. * * @param buckets the list of sorted buckets - * @param arr the original array + * @param array the original array + * @param the type of elements in the array * @return the sorted array */ - private static int[] concatenateBuckets(List> buckets, int[] arr) { + private > T[] concatenateBuckets(List> buckets, T[] array) { int index = 0; - for (List bucket : buckets) { - for (int value : bucket) { - arr[index++] = value; + for (List bucket : buckets) { + Collections.sort(bucket); + for (T element : bucket) { + array[index++] = element; } } - return arr; + return array; } /** - * Computes the hash value to determine which bucket an element should be placed in. + * The method computes the index of the bucket in which a given element should be placed. + * This is done by "normalizing" the element within the range of the array's minimum (min) and maximum (max) values, + * and then mapping this normalized value to a specific bucket index. * * @param element the element of the array * @param min the minimum value in the array + * @param max the maximum value in the array * @param numberOfBuckets the total number of buckets + * @param the type of elements in the array * @return the index of the bucket */ - private static int hash(final int element, final int min, final int numberOfBuckets) { - return (element - min) / numberOfBuckets; + private > int hash(final T element, final T min, final T max, final int numberOfBuckets) { + double range = max.compareTo(min); + double normalizedValue = element.compareTo(min) / range; + return (int) (normalizedValue * (numberOfBuckets - 1)); + } + + private > T findMin(T[] array) { + T min = array[0]; + for (T element : array) { + if (element.compareTo(min) < 0) { + min = element; + } + } + return min; + } + + private > T findMax(T[] array) { + T max = array[0]; + for (T element : array) { + if (element.compareTo(max) > 0) { + max = element; + } + } + return max; } } diff --git a/src/test/java/com/thealgorithms/sorts/BucketSortTest.java b/src/test/java/com/thealgorithms/sorts/BucketSortTest.java index 03e0870a4eda..a2dcb8cadfd9 100644 --- a/src/test/java/com/thealgorithms/sorts/BucketSortTest.java +++ b/src/test/java/com/thealgorithms/sorts/BucketSortTest.java @@ -1,88 +1,8 @@ package com.thealgorithms.sorts; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; - -import org.junit.jupiter.api.Test; - -public class BucketSortTest { - - @Test - public void sortSingleIntegerArray() { - int[] inputArray = {4}; - int[] expectedOutput = {4}; - int[] outputArray = BucketSort.bucketSort(inputArray); - assertArrayEquals(expectedOutput, outputArray); - } - - @Test - public void sortEmptyArray() { - int[] inputArray = {}; - int[] expectedOutput = {}; - int[] outputArray = BucketSort.bucketSort(inputArray); - assertArrayEquals(expectedOutput, outputArray); - } - - @Test - public void sortAlreadySortedArray() { - int[] inputArray = {1, 2, 3, 4, 5, 6, 7}; - int[] expectedOutput = {1, 2, 3, 4, 5, 6, 7}; - int[] outputArray = BucketSort.bucketSort(inputArray); - assertArrayEquals(expectedOutput, outputArray); - } - - @Test - public void sortNonDuplicateIntegerArray() { - int[] inputArray = {6, 1, 99, 27, 15, 23, 36}; - int[] expectedOutput = {1, 6, 15, 23, 27, 36, 99}; - int[] outputArray = BucketSort.bucketSort(inputArray); - assertArrayEquals(expectedOutput, outputArray); - } - - @Test - public void sortDuplicateIntegerArray() { - int[] inputArray = {6, 1, 27, 15, 23, 27, 36, 23}; - int[] expectedOutput = {1, 6, 15, 23, 23, 27, 27, 36}; - int[] outputArray = BucketSort.bucketSort(inputArray); - assertArrayEquals(expectedOutput, outputArray); - } - - @Test - public void sortNonDuplicateIntegerArrayWithNegativeNum() { - int[] inputArray = {6, -1, 99, 27, -15, 23, -36}; - int[] expectedOutput = {-36, -15, -1, 6, 23, 27, 99}; - int[] outputArray = BucketSort.bucketSort(inputArray); - assertArrayEquals(expectedOutput, outputArray); - } - - @Test - public void sortDuplicateIntegerArrayWithNegativeNum() { - int[] inputArray = {6, -1, 27, -15, 23, 27, -36, 23}; - int[] expectedOutput = {-36, -15, -1, 6, 23, 23, 27, 27}; - int[] outputArray = BucketSort.bucketSort(inputArray); - assertArrayEquals(expectedOutput, outputArray); - } - - @Test - public void sortLargeArray() { - int[] inputArray = {100, 50, -100, 200, 0, 150, -50}; - int[] expectedOutput = {-100, -50, 0, 50, 100, 150, 200}; - int[] outputArray = BucketSort.bucketSort(inputArray); - assertArrayEquals(expectedOutput, outputArray); - } - - @Test - public void sortArrayWithAllIdenticalElements() { - int[] inputArray = {5, 5, 5, 5, 5}; - int[] expectedOutput = {5, 5, 5, 5, 5}; - int[] outputArray = BucketSort.bucketSort(inputArray); - assertArrayEquals(expectedOutput, outputArray); - } - - @Test - public void sortArrayWithMixedPositiveAndNegativeNumbers() { - int[] inputArray = {-3, 0, 2, -2, 3, 1, -1}; - int[] expectedOutput = {-3, -2, -1, 0, 1, 2, 3}; - int[] outputArray = BucketSort.bucketSort(inputArray); - assertArrayEquals(expectedOutput, outputArray); +public class BucketSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new BucketSort(); } }