-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathradixSort.go
149 lines (135 loc) · 4.04 KB
/
radixSort.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package sort
import (
"cmp"
"math/bits"
"slices"
"unsafe"
)
// RadixSort implements radix sort using byte-by-byte sorting with 256 buckets for all integer types.
// It creates an internal copy of the supplied data, leading to one large allocation.
// The result is updated in-place and returned for convenience as well.
// Other data types such as float64, float32 and string are handled via a fallback to slices.Sort.
// Signed integers are handled by flipping the sign bit before and after sorting and treating them as unsigned integers.
// The computational complexity is O(n) with a space requirement of O(2n).
func RadixSort[T cmp.Ordered](items []T) []T {
// No need to sort slices with less than two items
if len(items) < 2 {
return items
}
var val T
switch any(val).(type) {
case uint64:
radixSortUint(any(items).([]uint64))
case uint32:
radixSortUint(any(items).([]uint32))
case uint16:
radixSortUint(any(items).([]uint16))
case uint8:
countingSort(any(items).([]uint8))
case uint:
radixSortUint(any(items).([]uint))
case uintptr:
radixSortUint(any(items).([]uintptr))
case int64:
uintslice := unsafe.Slice((*uint64)(unsafe.Pointer(unsafe.SliceData(items))), len(items))
for i := range uintslice {
uintslice[i] ^= 0x8000000000000000
}
radixSortUint(uintslice)
for i := range uintslice {
uintslice[i] ^= 0x8000000000000000
}
case int32:
uintslice := unsafe.Slice((*uint32)(unsafe.Pointer(unsafe.SliceData(items))), len(items))
for i := range uintslice {
uintslice[i] ^= 0x80000000
}
radixSortUint(uintslice)
for i := range uintslice {
uintslice[i] ^= 0x80000000
}
case int16:
uintslice := unsafe.Slice((*uint16)(unsafe.Pointer(unsafe.SliceData(items))), len(items))
for i := range uintslice {
uintslice[i] ^= 0x8000
}
radixSortUint(uintslice)
for i := range uintslice {
uintslice[i] ^= 0x8000
}
case int8:
uintslice := unsafe.Slice((*uint8)(unsafe.Pointer(unsafe.SliceData(items))), len(items))
for i := range uintslice {
uintslice[i] ^= 0x80
}
countingSort(uintslice)
for i := range uintslice {
uintslice[i] ^= 0x80
}
case int:
uintslice := unsafe.Slice((*uint)(unsafe.Pointer(unsafe.SliceData(items))), len(items))
var mask uint = 1 << (bits.UintSize - 1)
for i := range uintslice {
uintslice[i] ^= mask
}
radixSortUint(uintslice)
for i := range uintslice {
uintslice[i] ^= mask
}
default:
slices.Sort(items)
}
return items
}
// radixSortUint implements radix sort for all multi-byte unsigned integer types, adapting to their respective sizes
func radixSortUint[T uint64 | uint32 | uint16 | uint | uintptr](items []T) []T {
tmp := make([]T, len(items))
src := items
dst := tmp
var val T
bits := int(unsafe.Sizeof(val)) * 8
// Loop over the individual bytes of the unsigned integer type
for shift := 0; shift < bits; shift += 8 {
// Create buckets and count items
bucket := [256]int{}
for i := range items {
bucket[int(src[i]>>shift&0xFF)]++
}
// Add count from previous bucket
// The bucket values are used as indices for the sorted array and therefore have to be higher for buckets with higher sort values.
for i := 1; i < 256; i++ {
bucket[i] += bucket[i-1]
}
// Use the buckets indices when filling the sorted array
for i := len(items) - 1; i >= 0; i-- {
v := &bucket[int(src[i]>>shift&0xFF)]
*v--
dst[*v] = src[i]
}
// Swap source and destination for the next pass
src, dst = dst, src
}
return items
}
func countingSort(items []uint8) []uint8 {
tmp := make([]uint8, len(items))
// Create buckets and count items
bucket := [256]int{}
for i := range items {
bucket[int(items[i])]++
}
// Add count from previous bucket
// The bucket values are used as indices for the sorted array and therefore have to be higher for buckets with higher sort values.
for i := 1; i < 256; i++ {
bucket[i] += bucket[i-1]
}
// Use the buckets indices when filling the sorted array
for i := len(items) - 1; i >= 0; i-- {
v := &bucket[int(items[i])]
*v--
tmp[*v] = items[i]
}
// Copy result back
copy(items, tmp)
return items
}