github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/sorts/stdsortint/sort.go (about)

     1  /*
     2  
     3  Based on go std sort
     4  
     5  // Copyright 2009 The Go Authors. All rights reserved.
     6  // Use of this source code is governed by a BSD-style
     7  // license that can be found in the LICENSE file.
     8  */
     9  
    10  package stdsortint
    11  
    12  func dataless(data []int, i, k int) bool { return data[i] < data[k] }
    13  func dataswap(data []int, i, k int)      { data[i], data[k] = data[k], data[i] }
    14  
    15  // Insertion sort
    16  func insertionSort(data []int, a, b int) {
    17  	for i := a + 1; i < b; i++ {
    18  		for j := i; j > a && dataless(data, j, j-1); j-- {
    19  			dataswap(data, j, j-1)
    20  		}
    21  	}
    22  }
    23  
    24  // siftDown implements the heap property on data[lo, hi).
    25  // first is an offset into the array where the root of the heap lies.
    26  func siftDown(data []int, lo, hi, first int) {
    27  	root := lo
    28  	for {
    29  		child := 2*root + 1
    30  		if child >= hi {
    31  			break
    32  		}
    33  		if child+1 < hi && dataless(data, first+child, first+child+1) {
    34  			child++
    35  		}
    36  		if !dataless(data, first+root, first+child) {
    37  			return
    38  		}
    39  		dataswap(data, first+root, first+child)
    40  		root = child
    41  	}
    42  }
    43  
    44  func heapSort(data []int, a, b int) {
    45  	first := a
    46  	lo := 0
    47  	hi := b - a
    48  
    49  	// Build heap with greatest element at top.
    50  	for i := (hi - 1) / 2; i >= 0; i-- {
    51  		siftDown(data, i, hi, first)
    52  	}
    53  
    54  	// Pop elements, largest first, into end of data.
    55  	for i := hi - 1; i >= 0; i-- {
    56  		dataswap(data, first, first+i)
    57  		siftDown(data, lo, i, first)
    58  	}
    59  }
    60  
    61  // Quicksort, loosely following Bentley and McIlroy,
    62  // ``Engineering a Sort Function,'' SP&E November 1993.
    63  
    64  // medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
    65  func medianOfThree(data []int, m1, m0, m2 int) {
    66  	// sort 3 elements
    67  	if dataless(data, m1, m0) {
    68  		dataswap(data, m1, m0)
    69  	}
    70  	// data[m0] <= data[m1]
    71  	if dataless(data, m2, m1) {
    72  		dataswap(data, m2, m1)
    73  		// data[m0] <= data[m2] && data[m1] < data[m2]
    74  		if dataless(data, m1, m0) {
    75  			dataswap(data, m1, m0)
    76  		}
    77  	}
    78  	// now data[m0] <= data[m1] <= data[m2]
    79  }
    80  
    81  func swapRange(data []int, a, b, n int) {
    82  	for i := 0; i < n; i++ {
    83  		dataswap(data, a+i, b+i)
    84  	}
    85  }
    86  
    87  func doPivot(data []int, lo, hi int) (midlo, midhi int) {
    88  	m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow.
    89  	if hi-lo > 40 {
    90  		// Tukey's ``Ninther,'' median of three medians of three.
    91  		s := (hi - lo) / 8
    92  		medianOfThree(data, lo, lo+s, lo+2*s)
    93  		medianOfThree(data, m, m-s, m+s)
    94  		medianOfThree(data, hi-1, hi-1-s, hi-1-2*s)
    95  	}
    96  	medianOfThree(data, lo, m, hi-1)
    97  
    98  	// Invariants are:
    99  	//	data[lo] = pivot (set up by ChoosePivot)
   100  	//	data[lo < i < a] < pivot
   101  	//	data[a <= i < b] <= pivot
   102  	//	data[b <= i < c] unexamined
   103  	//	data[c <= i < hi-1] > pivot
   104  	//	data[hi-1] >= pivot
   105  	pivot := lo
   106  	a, c := lo+1, hi-1
   107  
   108  	for ; a < c && dataless(data, a, pivot); a++ {
   109  	}
   110  	b := a
   111  	for {
   112  		for ; b < c && !dataless(data, pivot, b); b++ { // data[b] <= pivot
   113  		}
   114  		for ; b < c && dataless(data, pivot, c-1); c-- { // data[c-1] > pivot
   115  		}
   116  		if b >= c {
   117  			break
   118  		}
   119  		// data[b] > pivot; data[c-1] <= pivot
   120  		dataswap(data, b, c-1)
   121  		b++
   122  		c--
   123  	}
   124  	// If hi-c<3 then there are duplicates (by property of median of nine).
   125  	// Let be a bit more conservative, and set border to 5.
   126  	protect := hi-c < 5
   127  	if !protect && hi-c < (hi-lo)/4 {
   128  		// Lets test some points for equality to pivot
   129  		dups := 0
   130  		if !dataless(data, pivot, hi-1) { // data[hi-1] = pivot
   131  			dataswap(data, c, hi-1)
   132  			c++
   133  			dups++
   134  		}
   135  		if !dataless(data, b-1, pivot) { // data[b-1] = pivot
   136  			b--
   137  			dups++
   138  		}
   139  		// m-lo = (hi-lo)/2 > 6
   140  		// b-lo > (hi-lo)*3/4-1 > 8
   141  		// ==> m < b ==> data[m] <= pivot
   142  		if !dataless(data, m, pivot) { // data[m] = pivot
   143  			dataswap(data, m, b-1)
   144  			b--
   145  			dups++
   146  		}
   147  		// if at least 2 points are equal to pivot, assume skewed distribution
   148  		protect = dups > 1
   149  	}
   150  	if protect {
   151  		// Protect against a lot of duplicates
   152  		// Add invariant:
   153  		//	data[a <= i < b] unexamined
   154  		//	data[b <= i < c] = pivot
   155  		for {
   156  			for ; a < b && !dataless(data, b-1, pivot); b-- { // data[b] == pivot
   157  			}
   158  			for ; a < b && dataless(data, a, pivot); a++ { // data[a] < pivot
   159  			}
   160  			if a >= b {
   161  				break
   162  			}
   163  			// data[a] == pivot; data[b-1] < pivot
   164  			dataswap(data, a, b-1)
   165  			a++
   166  			b--
   167  		}
   168  	}
   169  	// Swap pivot into middle
   170  	dataswap(data, pivot, b-1)
   171  	return b - 1, c
   172  }
   173  
   174  func quickSort(data []int, a, b, maxDepth int) {
   175  	for b-a > 12 { // Use ShellSort for slices <= 12 elements
   176  		if maxDepth == 0 {
   177  			heapSort(data, a, b)
   178  			return
   179  		}
   180  		maxDepth--
   181  		mlo, mhi := doPivot(data, a, b)
   182  		// Avoiding recursion on the larger subproblem guarantees
   183  		// a stack depth of at most lg(b-a).
   184  		if mlo-a < b-mhi {
   185  			quickSort(data, a, mlo, maxDepth)
   186  			a = mhi // i.e., quickSort(data, mhi, b)
   187  		} else {
   188  			quickSort(data, mhi, b, maxDepth)
   189  			b = mlo // i.e., quickSort(data, a, mlo)
   190  		}
   191  	}
   192  	if b-a > 1 {
   193  		// Do ShellSort pass with gap 6
   194  		// It could be written in this simplified form cause b-a <= 12
   195  		for i := a + 6; i < b; i++ {
   196  			if dataless(data, i, i-6) {
   197  				dataswap(data, i, i-6)
   198  			}
   199  		}
   200  		insertionSort(data, a, b)
   201  	}
   202  }
   203  
   204  // Sort sorts data.
   205  // It makes one call to data.Len to determine n, and O(n*log(n)) calls to
   206  // dataless data, and dataswap.data,  The sort is not guaranteed to be stable.
   207  func Sort(data []int) {
   208  	n := len(data)
   209  	quickSort(data, 0, n, maxDepth(n))
   210  }
   211  
   212  // maxDepth returns a threshold at which quicksort should switch
   213  // to heapsort. It returns 2*ceil(lg(n+1)).
   214  func maxDepth(n int) int {
   215  	var depth int
   216  	for i := n; i > 0; i >>= 1 {
   217  		depth++
   218  	}
   219  	return depth * 2
   220  }
   221  
   222  // IsSorted reports whether data is sorted.
   223  func IsSorted(data []int) bool {
   224  	n := len(data)
   225  	for i := n - 1; i > 0; i-- {
   226  		if dataless(data, i, i-1) {
   227  			return false
   228  		}
   229  	}
   230  	return true
   231  }