diff --git a/algorithms/sort/cycle_sort.py b/algorithms/sort/cycle_sort.py index 6e512c924..70425d22a 100644 --- a/algorithms/sort/cycle_sort.py +++ b/algorithms/sort/cycle_sort.py @@ -1,46 +1,67 @@ def cycle_sort(arr): """ - cycle_sort - This is based on the idea that the permutations to be sorted - can be decomposed into cycles, - and the results can be individually sorted by cycling. - - reference: https://en.wikipedia.org/wiki/Cycle_sort - - Average time complexity : O(N^2) - Worst case time complexity : O(N^2) + Sorts an array in place using the Cycle Sort algorithm. + + Cycle Sort is a comparison-based, in-place, and non-comparing sorting algorithm that is particularly useful for + minimizing the number of writes to the original array. It is based on the idea of dividing the array into + cycles and rotating the elements in each cycle to their correct positions. + + Parameters: + arr (list): A list of elements (numbers) to be sorted. The function modifies this list in place. + + Returns: + list: The sorted list. + + Complexity: + - Time Complexity: O(n^2) in the worst case, where n is the number of elements in the array. + - Space Complexity: O(1) since the sorting is done in place. + + Algorithm Steps: + 1. Iterate through each element in the array using a variable `cycleStart`. + 2. For each element, determine its correct position in the sorted array by counting how many elements + are less than the current element (`currElement`). + 3. If the current position (`currPos`) is the same as `cycleStart`, it means the element is already in + the correct position, so continue to the next element. + 4. If not, swap the current element with the element at its correct position. + 5. Repeat the process until the cycle is complete and all elements are in their sorted position. + + Note: This implementation currently does not count the number of writes made to the array, as indicated by + the commented-out `writes` variable. """ - len_arr = len(arr) - # Finding cycle to rotate. - for cur in range(len_arr - 1): - item = arr[cur] - - # Finding an indx to put items in. - index = cur - for i in range(cur + 1, len_arr): - if arr[i] < item: - index += 1 - - # Case of there is not a cycle - if index == cur: + for cycleStart in range(0, len(arr)-1): + currElement = arr[cycleStart] + currPos = cycleStart + + # Determine the position where the current element should go + for i in range(cycleStart+1, len(arr)): + if arr[i] < currElement: + currPos += 1 + + # If the element is already in the correct position, skip to the next + if currPos == cycleStart: continue - # Putting the item immediately right after the duplicate item or on the right. - while item == arr[index]: - index += 1 - arr[index], item = item, arr[index] + # Find the next position of the current element, in case of duplicates + while currElement == arr[currPos]: + currPos += 1 + + # Swap the current element to its correct position + arr[currPos], currElement = currElement, arr[currPos] + + # Continue the cycle for the element moved to its correct position + while currPos != cycleStart: + currPos = cycleStart + + # Determine the new position for the current element + for j in range(cycleStart+1, len(arr)): + if arr[j] < currElement: + currPos += 1 - # Rotating the remaining cycle. - while index != cur: + # Find the next position of the current element, in case of duplicates + while currElement == arr[currPos]: + currPos += 1 - # Finding where to put the item. - index = cur - for i in range(cur + 1, len_arr): - if arr[i] < item: - index += 1 + # Swap the current element to its correct position + arr[currPos], currElement = currElement, arr[currPos] - # After item is duplicated, put it in place or put it there. - while item == arr[index]: - index += 1 - arr[index], item = item, arr[index] return arr diff --git a/tests/test_sort.py b/tests/test_sort.py index c80290fdf..7ffe1c860 100644 --- a/tests/test_sort.py +++ b/tests/test_sort.py @@ -57,6 +57,30 @@ def test_counting_sort(self): def test_cycle_sort(self): self.assertTrue(is_sorted(cycle_sort([1, 3, 2, 5, 65, 23, 57, 1232]))) + def test_cycle_sort_already_sorted(self): + self.assertTrue(is_sorted(cycle_sort([1, 2, 3, 4, 5, 6, 7, 8, 9]))) + + def test_cycle_sort_reverse_sorted(self): + self.assertTrue(is_sorted(cycle_sort([9, 8, 7, 6, 5, 4, 3, 2, 1]))) + + def test_cycle_sort_single_element(self): + self.assertEqual(cycle_sort([42]), [42]) + + def test_cycle_sort_empty_array(self): + self.assertEqual(cycle_sort([]), []) + + def test_cycle_sort_with_duplicates(self): + self.assertTrue(is_sorted(cycle_sort([4, 1, 3, 2, 2, 5, 5, 1]))) + + def test_cycle_sort_large_random_array(self): + import random + random_array = random.sample(range(1, 1001), 100) + sorted_array = sorted(random_array) + self.assertEqual(cycle_sort(random_array), sorted_array) + + def test_cycle_sort_negative_numbers(self): + self.assertTrue(is_sorted(cycle_sort([-5, -1, -3, 2, 0, 1]))) + def test_exchange_sort(self): self.assertTrue(is_sorted(exchange_sort([1, 3, 2, 5, 65, 23, 57, 1232])))