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 }