diff --git a/DIRECTORY.md b/DIRECTORY.md index aa1005027..02ad74351 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -865,6 +865,7 @@ * [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) + * [CycleSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CycleSortTest.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) diff --git a/src/main/java/com/thealgorithms/sorts/CycleSort.java b/src/main/java/com/thealgorithms/sorts/CycleSort.java index 0852abfaa..e254dd810 100644 --- a/src/main/java/com/thealgorithms/sorts/CycleSort.java +++ b/src/main/java/com/thealgorithms/sorts/CycleSort.java @@ -1,100 +1,83 @@ package com.thealgorithms.sorts; -import static com.thealgorithms.sorts.SortUtils.less; -import static com.thealgorithms.sorts.SortUtils.print; - /** + * This class implements the cycle sort algorithm. + * Cycle sort is an in-place sorting algorithm, unstable, and efficient for scenarios with limited memory usage. * @author Podshivalov Nikita (https://github.com/nikitap492) */ class CycleSort implements SortAlgorithm { - + /** + * Sorts an array of comparable elements using the cycle sort algorithm. + * + * @param array the array to be sorted + * @param the type of elements in the array, must be comparable + * @return the sorted array + */ @Override - public > T[] sort(T[] arr) { - int n = arr.length; + public > T[] sort(T[] array) { + for (int cycleStart = 0; cycleStart <= array.length - 2; cycleStart++) { + T item = array[cycleStart]; - // traverse array elements - for (int j = 0; j <= n - 2; j++) { - // initialize item as starting point - T item = arr[j]; - - // Find position where we put the item. - int pos = j; - for (int i = j + 1; i < n; i++) { - if (less(arr[i], item)) { + // Find the position where we put the element + int pos = cycleStart; + for (int i = cycleStart + 1; i < array.length; i++) { + if (SortUtils.less(array[i], item)) { pos++; } } - // If item is already in correct position - if (pos == j) { + // If the item is already in the correct position + if (pos == cycleStart) { continue; } - // ignore all duplicate elements - while (item.compareTo(arr[pos]) == 0) { - pos += 1; + // Ignore all duplicate elements + while (item.compareTo(array[pos]) == 0) { + pos++; } - // put the item to it's right position - if (pos != j) { - item = replace(arr, pos, item); + // Put the item to its correct position + if (pos != cycleStart) { + item = replace(array, pos, item); } - // Rotate rest of the cycle - while (pos != j) { - pos = j; + // Rotate the rest of the cycle + while (pos != cycleStart) { + pos = cycleStart; - // Find position where we put the element - for (int i = j + 1; i < n; i++) { - if (less(arr[i], item)) { - pos += 1; + // Find the position where we put the element + for (int i = cycleStart + 1; i < array.length; i++) { + if (SortUtils.less(array[i], item)) { + pos++; } } - // ignore all duplicate elements - while (item.compareTo(arr[pos]) == 0) { - pos += 1; + // Ignore all duplicate elements + while (item.compareTo(array[pos]) == 0) { + pos++; } - // put the item to it's right position - if (item != arr[pos]) { - item = replace(arr, pos, item); + // Put the item to its correct position + if (item != array[pos]) { + item = replace(array, pos, item); } } } - - return arr; + return array; } - private > T replace(T[] arr, int pos, T item) { - T temp = item; - item = arr[pos]; - arr[pos] = temp; - return item; - } - - public static void main(String[] args) { - Integer[] arr = { - 4, - 23, - 6, - 78, - 1, - 26, - 11, - 23, - 0, - -6, - 3, - 54, - 231, - 9, - 12, - }; - CycleSort cycleSort = new CycleSort(); - cycleSort.sort(arr); - - System.out.println("After sort : "); - print(arr); + /** + * Replaces an element in the array with the given item and returns the replaced item. + * + * @param array the array in which the replacement will occur + * @param pos the position at which the replacement will occur + * @param item the item to be placed in the array + * @param the type of elements in the array, must be comparable + * @return the replaced item + */ + private > T replace(T[] array, int pos, T item) { + T replacedItem = array[pos]; + array[pos] = item; + return replacedItem; } } diff --git a/src/test/java/com/thealgorithms/sorts/CycleSortTest.java b/src/test/java/com/thealgorithms/sorts/CycleSortTest.java new file mode 100644 index 000000000..b8c3d1653 --- /dev/null +++ b/src/test/java/com/thealgorithms/sorts/CycleSortTest.java @@ -0,0 +1,8 @@ +package com.thealgorithms.sorts; + +public class CycleSortTest extends SortingAlgorithmTest { + @Override + SortAlgorithm getSortAlgorithm() { + return new CycleSort(); + } +} diff --git a/src/test/java/com/thealgorithms/sorts/SortingAlgorithmTest.java b/src/test/java/com/thealgorithms/sorts/SortingAlgorithmTest.java index e6aedc3f0..43de55018 100644 --- a/src/test/java/com/thealgorithms/sorts/SortingAlgorithmTest.java +++ b/src/test/java/com/thealgorithms/sorts/SortingAlgorithmTest.java @@ -1,11 +1,14 @@ package com.thealgorithms.sorts; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertIterableEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Objects; import org.junit.jupiter.api.Test; public abstract class SortingAlgorithmTest { @@ -171,4 +174,192 @@ public abstract class SortingAlgorithmTest { List sorted = getSortAlgorithm().sort(list); assertTrue(SortUtils.isSorted(sorted)); } + + @Test + public void shouldAcceptWhenArrayWithAllIdenticalValuesIsPassed() { + Integer[] array = {1, 1, 1, 1}; + Integer[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new Integer[] {1, 1, 1, 1}, sortedArray); + } + + @Test + public void shouldAcceptWhenListWithAllIdenticalValuesIsPassed() { + List list = Arrays.asList(1, 1, 1, 1); + List sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList(1, 1, 1, 1), sortedList); + } + + @Test + public void shouldAcceptWhenArrayWithMixedPositiveAndNegativeValuesIsPassed() { + Integer[] array = {-1, 3, -2, 5, 0}; + Integer[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new Integer[] {-2, -1, 0, 3, 5}, sortedArray); + } + + @Test + public void shouldAcceptWhenListWithMixedPositiveAndNegativeValuesIsPassed() { + List list = Arrays.asList(-1, 3, -2, 5, 0); + List sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList(-2, -1, 0, 3, 5), sortedList); + } + + @Test + public void shouldAcceptWhenArrayWithLargeNumbersIsPassed() { + Long[] array = {10000000000L, 9999999999L, 10000000001L}; + Long[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new Long[] {9999999999L, 10000000000L, 10000000001L}, sortedArray); + } + + @Test + public void shouldAcceptWhenListWithLargeNumbersIsPassed() { + List list = Arrays.asList(10000000000L, 9999999999L, 10000000001L); + List sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList(9999999999L, 10000000000L, 10000000001L), sortedList); + } + + @Test + public void shouldAcceptWhenArrayWithMaxIntegerValuesIsPassed() { + Integer[] array = {Integer.MAX_VALUE, Integer.MIN_VALUE, 0}; + Integer[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new Integer[] {Integer.MIN_VALUE, 0, Integer.MAX_VALUE}, sortedArray); + } + + @Test + public void shouldAcceptWhenListWithMaxIntegerValuesIsPassed() { + List list = Arrays.asList(Integer.MAX_VALUE, Integer.MIN_VALUE, 0); + List sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE), sortedList); + } + + @Test + public void shouldAcceptWhenArrayWithMinIntegerValuesIsPassed() { + Integer[] array = {Integer.MIN_VALUE, Integer.MAX_VALUE, 0}; + Integer[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new Integer[] {Integer.MIN_VALUE, 0, Integer.MAX_VALUE}, sortedArray); + } + + @Test + public void shouldAcceptWhenListWithMinIntegerValuesIsPassed() { + List list = Arrays.asList(Integer.MIN_VALUE, Integer.MAX_VALUE, 0); + List sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList(Integer.MIN_VALUE, 0, Integer.MAX_VALUE), sortedList); + } + + @Test + public void shouldAcceptWhenArrayWithSpecialCharactersIsPassed() { + String[] array = {"!", "@", "#", "$"}; + String[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new String[] {"!", "#", "$", "@"}, sortedArray); + } + + @Test + public void shouldAcceptWhenListWithSpecialCharactersIsPassed() { + List list = Arrays.asList("!", "@", "#", "$"); + List sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList("!", "#", "$", "@"), sortedList); + } + + @Test + public void shouldAcceptWhenArrayWithMixedCaseStringsIsPassed() { + String[] array = {"apple", "Banana", "cherry", "Date"}; + String[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new String[] {"Banana", "Date", "apple", "cherry"}, sortedArray); + } + + @Test + public void shouldAcceptWhenListWithMixedCaseStringsIsPassed() { + List list = Arrays.asList("apple", "Banana", "cherry", "Date"); + List sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList("Banana", "Date", "apple", "cherry"), sortedList); + } + + @Test + public void shouldHandleArrayWithNullValues() { + Integer[] array = {3, null, 2, null, 1}; + org.junit.jupiter.api.Assertions.assertThrows(NullPointerException.class, () -> getSortAlgorithm().sort(array)); + } + + @Test + public void shouldHandleListWithNullValues() { + List list = Arrays.asList(3, null, 2, null, 1); + org.junit.jupiter.api.Assertions.assertThrows(NullPointerException.class, () -> getSortAlgorithm().sort(list)); + } + + static class CustomObject implements Comparable { + int value; + + CustomObject(int value) { + this.value = value; + } + + @Override + public int compareTo(CustomObject o) { + return Integer.compare(this.value, o.value); + } + + @Override + public String toString() { + return "CustomObject{" + + "value=" + value + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CustomObject that = (CustomObject) o; + return value == that.value; + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + } + + @Test + public void shouldHandleArrayOfCustomObjects() { + CustomObject[] array = {new CustomObject(3), new CustomObject(1), new CustomObject(2)}; + CustomObject[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new CustomObject[] {new CustomObject(1), new CustomObject(2), new CustomObject(3)}, sortedArray); + } + + @Test + public void shouldHandleListOfCustomObjects() { + List list = Arrays.asList(new CustomObject(3), new CustomObject(1), new CustomObject(2)); + List sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList(new CustomObject(1), new CustomObject(2), new CustomObject(3)), sortedList); + } + + @Test + public void shouldHandleArrayOfFloatingPointNumbers() { + Double[] array = {3.3, 2.2, 1.1, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}; + Double[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new Double[] {Double.NEGATIVE_INFINITY, 1.1, 2.2, 3.3, Double.POSITIVE_INFINITY, Double.NaN}, sortedArray); + } + + @Test + public void shouldHandleListOfFloatingPointNumbers() { + List list = Arrays.asList(3.3, 2.2, 1.1, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY); + List sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList(Double.NEGATIVE_INFINITY, 1.1, 2.2, 3.3, Double.POSITIVE_INFINITY, Double.NaN), sortedList); + } + + @Test + public void shouldHandleArrayWithEmptyStrings() { + String[] array = {"apple", "", "banana", ""}; + String[] sortedArray = getSortAlgorithm().sort(array); + assertArrayEquals(new String[] {"", "", "apple", "banana"}, sortedArray); + } + + @Test + public void shouldHandleListWithEmptyStrings() { + List list = Arrays.asList("apple", "", "banana", ""); + List sortedList = getSortAlgorithm().sort(list); + assertEquals(Arrays.asList("", "", "apple", "banana"), sortedList); + } }