Skip to content

refactor: cleanup CycleSort #5271

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

Merged
merged 16 commits into from
Jul 13, 2024
Merged
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
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
121 changes: 52 additions & 69 deletions src/main/java/com/thealgorithms/sorts/CycleSort.java
Original file line number Diff line number Diff line change
@@ -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 <T> the type of elements in the array, must be comparable
* @return the sorted array
*/
@Override
public <T extends Comparable<T>> T[] sort(T[] arr) {
int n = arr.length;

// 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)) {
public <T extends Comparable<T>> T[] sort(T[] array) {
for (int cycleStart = 0; cycleStart <= array.length - 2; cycleStart++) {
T item = array[cycleStart];

// 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;
}

private <T extends Comparable<T>> T replace(T[] arr, int pos, T item) {
T temp = item;
item = arr[pos];
arr[pos] = temp;
return item;
return array;
}

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 <T> the type of elements in the array, must be comparable
* @return the replaced item
*/
private <T extends Comparable<T>> T replace(T[] array, int pos, T item) {
T replacedItem = array[pos];
array[pos] = item;
return replacedItem;
}
}
8 changes: 8 additions & 0 deletions src/test/java/com/thealgorithms/sorts/CycleSortTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.thealgorithms.sorts;

public class CycleSortTest extends SortingAlgorithmTest {
@Override
SortAlgorithm getSortAlgorithm() {
return new CycleSort();
}
}
191 changes: 191 additions & 0 deletions src/test/java/com/thealgorithms/sorts/SortingAlgorithmTest.java
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -171,4 +174,192 @@ void shouldAcceptWhenRandomListIsPassed() {
List<Double> 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<Integer> list = Arrays.asList(1, 1, 1, 1);
List<Integer> 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<Integer> list = Arrays.asList(-1, 3, -2, 5, 0);
List<Integer> 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<Long> list = Arrays.asList(10000000000L, 9999999999L, 10000000001L);
List<Long> 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<Integer> list = Arrays.asList(Integer.MAX_VALUE, Integer.MIN_VALUE, 0);
List<Integer> 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<Integer> list = Arrays.asList(Integer.MIN_VALUE, Integer.MAX_VALUE, 0);
List<Integer> 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<String> list = Arrays.asList("!", "@", "#", "$");
List<String> 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<String> list = Arrays.asList("apple", "Banana", "cherry", "Date");
List<String> 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<Integer> list = Arrays.asList(3, null, 2, null, 1);
org.junit.jupiter.api.Assertions.assertThrows(NullPointerException.class, () -> getSortAlgorithm().sort(list));
}

static class CustomObject implements Comparable<CustomObject> {
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<CustomObject> list = Arrays.asList(new CustomObject(3), new CustomObject(1), new CustomObject(2));
List<CustomObject> 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<Double> list = Arrays.asList(3.3, 2.2, 1.1, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
List<Double> 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<String> list = Arrays.asList("apple", "", "banana", "");
List<String> sortedList = getSortAlgorithm().sort(list);
assertEquals(Arrays.asList("", "", "apple", "banana"), sortedList);
}
}