Skip to content
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

Refactor cycle_sort and add comprehensive test cases #929

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
95 changes: 58 additions & 37 deletions algorithms/sort/cycle_sort.py
Original file line number Diff line number Diff line change
@@ -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
24 changes: 24 additions & 0 deletions tests/test_sort.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])))
Expand Down