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  }