github.com/searKing/golang/go@v1.2.74/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  	"container/heap"
     9  	"sort"
    10  
    11  	sort_ "github.com/searKing/golang/go/sort"
    12  	"golang.org/x/exp/constraints"
    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 constraints.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  // PartialSort rearranges elements such that the range [0, m)
    68  // contains the sorted m smallest elements in the range [first, data.Len).
    69  // The order of equal elements is not guaranteed to be preserved.
    70  // The order of the remaining elements in the range [m, data.Len) is unspecified.
    71  // PartialSort modifies the contents of the slice s; it does not create a new slice.
    72  func PartialSort[S ~[]E, E constraints.Ordered](s S, k int) {
    73  	if s == nil {
    74  		return
    75  	}
    76  
    77  	if k <= 0 {
    78  		return
    79  	}
    80  	if k >= len(s) {
    81  		sort.Sort(SortSlice[E](s))
    82  		return
    83  	}
    84  
    85  	var ss = make(S, k)
    86  	copy(ss, s[:k])
    87  	h := MaxHeap[E](ss)
    88  	heap.Init(&h)
    89  	for i, v := range s[k:] {
    90  		heap.Push(&h, v)
    91  		vv := heap.Pop(&h).(E)
    92  		s[i+k] = vv
    93  	}
    94  
    95  	for h.Len() > 0 {
    96  		s[h.Len()-1] = heap.Pop(&h).(E)
    97  	}
    98  	return
    99  }
   100  
   101  func PartialSortFunc[S ~[]E, E any](s S, k int, cmp func(E, E) int) {
   102  	if s == nil {
   103  		return
   104  	}
   105  
   106  	if k <= 0 {
   107  		return
   108  	}
   109  
   110  	if k >= len(s) {
   111  		k = len(s)
   112  	}
   113  
   114  	var ss = make(S, k)
   115  	copy(ss, s[:k])
   116  
   117  	if k <= 0 {
   118  		return
   119  	}
   120  	// MaxHeap
   121  	h := NewHeapFunc(ss, func(a E, b E) int {
   122  		if cmp == nil {
   123  			return 0
   124  		}
   125  		return -cmp(a, b)
   126  	})
   127  	heap.Init(h)
   128  	for i, v := range s[k:] {
   129  		heap.Push(h, v)
   130  		vv := heap.Pop(h).(E)
   131  		s[i+k] = vv
   132  	}
   133  
   134  	for h.Len() > 0 {
   135  		s[h.Len()-1] = heap.Pop(h).(E)
   136  	}
   137  	return
   138  }
   139  
   140  // IsPartialSorted reports whether data[:k] is partial sorted, as top k of data[:].
   141  func IsPartialSorted[S ~[]E, E constraints.Ordered](s S, k int) bool {
   142  	return sort_.IsPartialSorted(SortSlice[E](s), k)
   143  }
   144  
   145  // Convenience types for common cases
   146  
   147  // SortSlice attaches the methods of Interface to []E, sorting in increasing order.
   148  type SortSlice[E constraints.Ordered] []E
   149  
   150  func (x SortSlice[E]) Len() int { return len(x) }
   151  
   152  // Less reports whether x[i] should be ordered before x[j], as required by the sort Interface.
   153  // Note that floating-point comparison by itself is not a transitive relation: it does not
   154  // report a consistent ordering for not-a-number (NaN) values.
   155  // This implementation of Less places NaN values before any others, by using:
   156  //
   157  //	x[i] < x[j] || (math.IsNaN(x[i]) && !math.IsNaN(x[j]))
   158  func (x SortSlice[E]) Less(i, j int) bool { return x[i] < x[j] || (isNaN(x[i]) && !isNaN(x[j])) }
   159  func (x SortSlice[E]) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
   160  
   161  // Sort is a convenience method: x.Sort() calls Sort(x).
   162  func (x SortSlice[E]) Sort() { sort.Sort(x) }
   163  
   164  // isNaN is a copy of math.IsNaN to avoid a dependency on the math package.
   165  func isNaN[E constraints.Ordered](f E) bool {
   166  	return f != f
   167  }