Skip to content

feat: add cycle sort algorithm #634

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 5 commits into from
May 1, 2023
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
58 changes: 58 additions & 0 deletions sort/cyclesort.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package sort

import (
"github.com/TheAlgorithms/Go/constraints"
)

// Cycle sort is an in-place, unstable sorting algorithm that is particularly useful
// when sorting arrays containing elements with a small range of values. It is theoretically
// optimal in terms of the total number of writes to the original array.
func Cycle[T constraints.Number](arr []T) []T {
counter, cycle, len := 0, 0, len(arr)
// Early return if the array too small
if len <= 1 {
return arr
}

for cycle = 0; cycle < len-1; cycle++ {
elem := arr[cycle]
// Find total smaller elements to right
pos := cycle
for counter = cycle + 1; counter < len; counter++ {
if arr[counter] < elem {
pos++
}
}
// In case this element is already in correct position, let's skip processing
if pos == cycle {
continue
}
// In case we have same elements, we want to skip to the end of that list as well, ignoring order
// This makes the algorithm unstable for composite elements
for elem == arr[pos] {
pos++
}
// Now let us put the item to it's right position
arr[pos], elem = elem, arr[pos]

//We need to rotate the array till we have reached the start of the cycle again
for pos != cycle {
pos = cycle
// Find smaller elements to right again
for counter = cycle + 1; counter < len; counter++ {
if arr[counter] < elem {
pos++
}
}
for elem == arr[pos] {
pos++
}
//We can do this unconditionally, but the check helps prevent redundant writes to the array
if elem != arr[pos] {
arr[pos], elem = elem, arr[pos]
}
}
}

return arr
}
8 changes: 8 additions & 0 deletions sort/sorts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ func TestPatience(t *testing.T) {
testFramework(t, sort.Patience[int])
}

func TestCycle(t *testing.T) {
testFramework(t, sort.Cycle[int])
}

//END TESTS

func benchmarkFramework(b *testing.B, f func(arr []int) []int) {
Expand Down Expand Up @@ -286,3 +290,7 @@ func BenchmarkPigeonhole(b *testing.B) {
func BenchmarkPatience(b *testing.B) {
benchmarkFramework(b, sort.Patience[int])
}

func BenchmarkCycle(b *testing.B) {
benchmarkFramework(b, sort.Cycle[int])
}