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

feat: add Timsort sorting algorithm #692

Merged
merged 9 commits into from
Oct 27, 2023
10 changes: 9 additions & 1 deletion sort/sorts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,11 @@ func TestCycle(t *testing.T) {
testFramework(t, sort.Cycle[int])
}

//END TESTS
func TestTimsort(t *testing.T) {
testFramework(t, sort.Timsort[int])
}

// END TESTS

func benchmarkFramework(b *testing.B, f func(arr []int) []int) {
var sortTests = []struct {
Expand Down Expand Up @@ -320,3 +324,7 @@ func BenchmarkPatience(b *testing.B) {
func BenchmarkCycle(b *testing.B) {
benchmarkFramework(b, sort.Cycle[int])
}

func BenchmarkTimsort(b *testing.B) {
benchmarkFramework(b, sort.Timsort[int])
}
71 changes: 71 additions & 0 deletions sort/timsort.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Implementation of Timsort algorithm
// Reference: https://en.wikipedia.org/wiki/Timsort

package sort

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

const runSizeThreshold = 8

// Timsort is a simple generic implementation of Timsort algorithm.
func Timsort[T constraints.Ordered](data []T) []T {
runSize := calculateRunSize(len(data))
insertionSortRuns(data, runSize)
mergeRuns(data, runSize)
return data
}

// calculateRunSize returns a run size parameter that is further used
// to slice the data slice.
func calculateRunSize(dataLength int) int {
remainder := 0
for dataLength >= runSizeThreshold {
if dataLength%2 == 1 {
remainder = 1
}

dataLength = dataLength / 2
}

return dataLength + remainder
}

// insertionSortRuns runs insertion sort on all the data runs one by one.
func insertionSortRuns[T constraints.Ordered](data []T, runSize int) {
raklaptudirm marked this conversation as resolved.
Show resolved Hide resolved
for lower := 0; lower < len(data); lower += runSize {
upper := lower + runSize
if upper >= len(data) {
upper = len(data)
}

Insertion(data[lower:upper])
}
}

// mergeRuns merge sorts all the data runs into a single sorted data slice.
func mergeRuns[T constraints.Ordered](data []T, runSize int) {
for size := runSize; size < len(data); size *= 2 {
for lowerBound := 0; lowerBound < len(data); lowerBound += size * 2 {
middleBound := lowerBound + size - 1
upperBound := lowerBound + 2*size - 1
if len(data)-1 < upperBound {
upperBound = len(data) - 1
}

mergeRun(data, lowerBound, middleBound, upperBound)
}
}
}

// mergeRun uses merge sort to sort adjacent data runs.
func mergeRun[T constraints.Ordered](data []T, lower, mid, upper int) {
left := data[lower : mid+1]
right := data[mid+1 : upper+1]
merged := merge(left, right)
// rewrite original data slice values with sorted values from merged slice
for i, value := range merged {
data[lower+i] = value
}
}