-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add Timsort sorting algorithm (#692)
* 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
1 parent
855c430
commit e33cfa9
Showing
2 changed files
with
80 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |