github.com/searKing/golang/go@v1.2.117/exp/slices/sort.go (about) 1 // Copyright 2022 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package slices 6 7 import ( 8 "cmp" 9 "container/heap" 10 "sort" 11 12 sort_ "github.com/searKing/golang/go/sort" 13 ) 14 15 // LinearSearch searches for target in a sorted slice and returns the position 16 // where target is found, or the position where target would appear in the 17 // sort order; it also returns a bool saying whether the target is really found 18 // in the slice. The slice must be sorted in increasing order. 19 // Note: Binary-search was compared using the benchmarks. The following 20 // code is equivalent to the linear search above: 21 // 22 // pos := sort.Search(len(x), func(i int) bool { 23 // return target < x[i] 24 // }) 25 // 26 // The binary search wins for very large boundary sets, but 27 // the linear search performs better up through arrays between 28 // 256 and 512 elements, so we continue to prefer linear search. 29 func LinearSearch[S ~[]E, E cmp.Ordered](x S, target E) (int, bool) { 30 // search returns the leftmost position where f returns true, or len(x) if f 31 // returns false for all x. This is the insertion position for target in x, 32 // and could point to an element that's either == target or not. 33 pos := search(len(x), func(i int) bool { 34 return x[i] >= target 35 }) 36 if pos >= len(x) || x[pos] != target { 37 return pos, false 38 } else { 39 return pos, true 40 } 41 } 42 43 // LinearSearchFunc works like LinearSearch, but uses a custom comparison 44 // function. The slice must be sorted in increasing order, where "increasing" is 45 // defined by cmp. cmp(a, b) is expected to return an integer comparing the two 46 // parameters: 0 if a == b, a negative number if a < b and a positive number if 47 // a > b. 48 func LinearSearchFunc[S ~[]E, E any](x S, target E, cmp func(E, E) int) (int, bool) { 49 pos := search(len(x), func(i int) bool { return cmp(x[i], target) >= 0 }) 50 if pos >= len(x) || cmp(x[pos], target) != 0 { 51 return pos, false 52 } else { 53 return pos, true 54 } 55 } 56 57 func search(n int, f func(int) bool) int { 58 pos := n 59 for i := 0; i < n; i++ { 60 if f(i) { 61 return i // preserves f(i) == true 62 } 63 } 64 return pos 65 } 66 67 // SearchMin uses liner search to find and return the smallest index i 68 // in [0, n) at which f(i) is min(f...), assuming that on the range [0, n), 69 // Search returns the first true index. If there is no such index, Search returns n. 70 // (Note that the "not found" return value is not -1 as in, for instance, 71 // strings.Index.) 72 // SearchMin calls f(i) only for i in the range [0, n). 73 func SearchMin[S ~[]E, E cmp.Ordered](s S) int { 74 if len(s) == 0 { 75 return 0 76 } 77 m := s[0] 78 mi := 0 79 for i, v := range s[1:] { 80 if v < m { 81 m = v 82 mi = i + 1 83 } 84 } 85 return mi 86 } 87 88 // SearchMinFunc works like SearchMin, but uses a custom comparison 89 // function. The slice is sorted in any order, where "increasing" is 90 // defined by cmp. cmp(a, b) is expected to return an integer comparing the two 91 // parameters: 0 if a == b, a negative number if a < b and a positive number if 92 // a > b. 93 func SearchMinFunc[S ~[]E, E any](s S, cmp func(E, E) int) int { 94 if len(s) == 0 { 95 return 0 96 } 97 m := s[0] 98 mi := 0 99 for i, v := range s[1:] { 100 if cmp != nil && cmp(v, m) < 0 { 101 m = v 102 mi = i + 1 103 } 104 } 105 return mi 106 } 107 108 // SearchMax uses liner search to find and return the biggest index i 109 // in [0, n) at which f(i) is min(f...), assuming that on the range [0, n), 110 // Search returns the first true index. If there is no such index, Search returns n. 111 // (Note that the "not found" return value is not -1 as in, for instance, 112 // strings.Index.) 113 // Search calls f(i) only for i in the range [0, n). 114 func SearchMax[S ~[]E, E cmp.Ordered](s S) int { 115 if len(s) == 0 { 116 return 0 117 } 118 m := s[0] 119 mi := 0 120 for i, v := range s[1:] { 121 if m < v { 122 m = v 123 mi = i + 1 124 } 125 } 126 return mi 127 } 128 129 // PartialSort rearranges elements such that the range [0, m) 130 // contains the sorted m smallest elements in the range [first, data.Len). 131 // The order of equal elements is not guaranteed to be preserved. 132 // The order of the remaining elements in the range [m, data.Len) is unspecified. 133 // 134 // The sort is not guaranteed to be stable: equal elements 135 // may be reversed from their original order. 136 // 137 // PartialSort modifies the contents of the slice s; it does not create a new slice. 138 func PartialSort[S ~[]E, E cmp.Ordered](s S, k int) { 139 if s == nil { 140 return 141 } 142 143 if k <= 0 { 144 return 145 } 146 if k >= len(s) { 147 sort.Sort(SortSlice[E](s)) 148 return 149 } 150 151 h := MaxHeap[E](s[:k]) 152 heap.Init(&h) 153 154 { 155 heap.Push(&h, s[k]) 156 s[k] = heap.Pop(&h).(E) 157 } 158 159 sk := s[k] // backup, s[k] will be used as cache for max heap push and pop 160 161 for i, v := range s[k+1:] { 162 heap.Push(&h, v) 163 vv := heap.Pop(&h).(E) 164 s[i+k+1] = vv 165 } 166 167 s[k] = sk 168 169 for h.Len() > 0 { 170 s[h.Len()-1] = heap.Pop(&h).(E) 171 } 172 return 173 } 174 175 // PartialSortFunc works like PartialSort, but uses a custom comparison 176 // function. The slice must be sorted in increasing order, where "increasing" is 177 // defined by cmp. cmp(a, b) is expected to return an integer comparing the two 178 // parameters: 0 if a == b, a negative number if a < b and a positive number if 179 // a > b. 180 func PartialSortFunc[S ~[]E, E any](s S, k int, cmp func(E, E) int) { 181 if s == nil { 182 return 183 } 184 185 if k <= 0 { 186 return 187 } 188 189 if k >= len(s) { 190 k = len(s) 191 } 192 193 if k <= 0 { 194 return 195 } 196 if k == len(s) { 197 // MinHeap 198 sort.Slice(s, func(i, j int) bool { 199 if cmp == nil { 200 return false 201 } 202 return cmp(s[i], s[j]) < 0 203 }) 204 return 205 } 206 207 // MaxHeap 208 h := NewHeapFunc(s[:k], func(a E, b E) int { 209 if cmp == nil { 210 return 0 211 } 212 return -cmp(a, b) 213 }) 214 heap.Init(h) 215 216 if k == len(s) { 217 return 218 } 219 220 { 221 heap.Push(h, s[k]) 222 s[k] = heap.Pop(h).(E) 223 } 224 225 sk := s[k] // backup, s[k] will be used as cache for max heap push and pop 226 227 for i, v := range s[k+1:] { 228 heap.Push(h, v) 229 vv := heap.Pop(h).(E) 230 s[i+k+1] = vv 231 } 232 233 s[k] = sk 234 235 for h.Len() > 0 { 236 s[h.Len()-1] = heap.Pop(h).(E) 237 } 238 return 239 } 240 241 // IsPartialSorted reports whether data[:k] is partial sorted, as top k of data[:]. 242 func IsPartialSorted[S ~[]E, E cmp.Ordered](s S, k int) bool { 243 return sort_.IsPartialSorted(SortSlice[E](s), k) 244 } 245 246 // Convenience types for common cases 247 248 // SortSlice attaches the methods of Interface to []E, sorting in increasing order. 249 type SortSlice[E cmp.Ordered] []E 250 251 func (x SortSlice[E]) Len() int { return len(x) } 252 253 // Less reports whether x[i] should be ordered before x[j], as required by the sort Interface. 254 // Note that floating-point comparison by itself is not a transitive relation: it does not 255 // report a consistent ordering for not-a-number (NaN) values. 256 // This implementation of Less places NaN values before any others, by using: 257 // 258 // x[i] < x[j] || (math.IsNaN(x[i]) && !math.IsNaN(x[j])) 259 func (x SortSlice[E]) Less(i, j int) bool { return x[i] < x[j] || (isNaN(x[i]) && !isNaN(x[j])) } 260 func (x SortSlice[E]) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 261 262 // Sort is a convenience method: x.Sort() calls Sort(x). 263 func (x SortSlice[E]) Sort() { sort.Sort(x) } 264 265 // isNaN is a copy of math.IsNaN to avoid a dependency on the math package. 266 func isNaN[E cmp.Ordered](f E) bool { 267 return f != f 268 }