2017-08-29 22:27:42 +00:00
|
|
|
// Sorting algorithms. May also contain deduplication operations.
|
|
|
|
package sort
|
|
|
|
|
|
|
|
import (
|
2017-09-11 10:12:43 +00:00
|
|
|
"context"
|
2017-08-29 22:27:42 +00:00
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
Optimize merging/sorting
Previously we sorted a list e.g. [3,2,1]
that we already got, then fetched another
list e.g. [6,7,8,2]. Instead of just
sorting the new list and manually merging it
we would sort the appended lists:
[1,2,3, 6,7,8,2]
Despite mergesort, this is now optimized in
a two-step process:
* ONLY sort the new list
* call a Merge function on both lists, that assumes
sorted and deduplicated lists
This should be faster.
2017-09-11 11:15:43 +00:00
|
|
|
// Merge too already sorted and deduplicated lists.
|
|
|
|
func MergeLists(l []int, r []int) []int {
|
|
|
|
n := []int{}
|
|
|
|
|
|
|
|
i := 0
|
|
|
|
j := 0
|
|
|
|
for {
|
|
|
|
if i == len(l) && j == len(r) {
|
|
|
|
break
|
|
|
|
} else if i == len(l) {
|
|
|
|
n = append(n, r[j:]...)
|
|
|
|
break
|
|
|
|
} else if j == len(r) {
|
|
|
|
n = append(n, l[i:]...)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if l[i] < r[j] {
|
|
|
|
n = append(n, l[i])
|
|
|
|
i++
|
|
|
|
} else if l[i] > r[j] {
|
|
|
|
n = append(n, r[j])
|
|
|
|
j++
|
|
|
|
} else if l[i] == r[j] {
|
|
|
|
n = append(n, l[i])
|
|
|
|
i++
|
|
|
|
j++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
2017-08-29 22:27:42 +00:00
|
|
|
// Mergesorts and deduplicates the list.
|
2017-09-11 10:12:43 +00:00
|
|
|
func SortedAndDedup(ctx context.Context, list []int) (res []int, err error) {
|
|
|
|
sorted, err := Mergesort(ctx, list)
|
2017-08-29 22:27:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
deduped := dedupSortedList(sorted)
|
|
|
|
return deduped, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deduplicate the sorted list and return a new one with a potentially different
|
|
|
|
// size.
|
|
|
|
func dedupSortedList(list []int) []int {
|
|
|
|
newList := []int{}
|
|
|
|
if len(list) <= 1 {
|
|
|
|
return list
|
|
|
|
}
|
|
|
|
var prev int = list[0]
|
|
|
|
newList = append(newList, list[0])
|
|
|
|
for i := 1; i < len(list); i++ {
|
|
|
|
if prev != list[i] {
|
|
|
|
newList = append(newList, list[i])
|
|
|
|
}
|
|
|
|
prev = list[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
return newList
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mergesorts the given list and returns it as a result. The input list
|
|
|
|
// is not modified.
|
|
|
|
// The algorithm is a bottom-up iterative version and not explained
|
|
|
|
// in detail here.
|
2017-09-11 10:12:43 +00:00
|
|
|
func Mergesort(ctx context.Context, list []int) (res []int, err error) {
|
2017-08-29 22:27:42 +00:00
|
|
|
newList := append([]int{}, list...)
|
|
|
|
temp := append([]int{}, list...)
|
|
|
|
n := len(newList)
|
|
|
|
|
|
|
|
for m := 1; m < (n - 1); m = 2 * m {
|
|
|
|
for i := 0; i < (n - 1); i += 2 * m {
|
|
|
|
select {
|
2017-09-11 10:12:43 +00:00
|
|
|
case <-ctx.Done():
|
2017-08-29 22:27:42 +00:00
|
|
|
return nil, fmt.Errorf("Sorting timed out")
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
from := i
|
|
|
|
mid := i + m - 1
|
|
|
|
to := min(i+2*m-1, n-1)
|
|
|
|
|
2017-09-11 10:12:43 +00:00
|
|
|
merge(ctx, newList, temp, from, mid, to)
|
2017-08-29 22:27:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return newList, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// The merge part of the mergesort.
|
2017-09-11 10:12:43 +00:00
|
|
|
func merge(ctx context.Context, list []int, temp []int, from int, mid int, to int) {
|
2017-08-29 22:27:42 +00:00
|
|
|
k := from
|
|
|
|
i := from
|
|
|
|
j := mid + 1
|
|
|
|
|
|
|
|
for i <= mid && j <= to {
|
|
|
|
if list[i] < list[j] {
|
|
|
|
temp[k] = list[i]
|
|
|
|
i++
|
|
|
|
} else {
|
|
|
|
temp[k] = list[j]
|
|
|
|
j++
|
|
|
|
}
|
|
|
|
k++
|
|
|
|
}
|
|
|
|
|
|
|
|
for i <= mid && i < len(temp) {
|
|
|
|
temp[k] = list[i]
|
|
|
|
i++
|
|
|
|
k++
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := from; i <= to; i++ {
|
|
|
|
list[i] = temp[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the minimum of two integers.
|
|
|
|
func min(l int, r int) int {
|
|
|
|
if l < r {
|
|
|
|
return l
|
|
|
|
} else {
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
}
|