Skip to content

Commit

Permalink
feat: add Timsort sorting algorithm (#692)
Browse files Browse the repository at this point in the history
* feat: add timsort sorting algorithm implementation

* test: add timsort sorting algorithm to tests

* chore: remove left-over print statement

* refactor: change insertionSortRun temp variable name

* docs: add concise documentation to timsort algorithm

* refactor: reuse insertion sort algorithm

* refactor: reuse merge sort algorithm helper function

* refactor: remove slice copying in merge run

---------

Co-authored-by: Taj <tjgurwara99@users.noreply.github.com>
  • Loading branch information
stanislav-zeman and tjgurwara99 authored Oct 27, 2023
1 parent 855c430 commit e33cfa9
Showing 2 changed files with 80 additions and 1 deletion.
10 changes: 9 additions & 1 deletion sort/sorts_test.go
Original file line number Diff line number Diff line change
@@ -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 {
@@ -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) {
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
}
}

0 comments on commit e33cfa9

Please sign in to comment.