// Sort按Less方法确定的升序对数据进行排序。它对数据进行一次调用。 // Len来确定对数据的n和O(n*log(n))调用Less和Swap。排序不能保证是稳定的。 funcSort(data Interface) { n := data.Len() quickSort(data, 0, n, maxDepth(n)) }
// maxDepth返回一个阈值,在该阈值下,快速排序应切换到堆排序。它返回2*ceil(lg(n+1))。 // 也就是返回要排序的个数组成二叉树之后的深度 * 2 funcmaxDepth(n int)int { var depth int for i := n; i > 0; i >>= 1 { depth++ } return depth * 2 }
// siftDown implements the heap property on data[lo:hi]. // first is an offset into the array where the root of the heap lies. // 在data[lo:hi]上实现了堆属性。first是堆根所在的数组的偏移量。 funcsiftDown(data Interface, lo, hi, first int) { root := lo for { // 左孩子下标 child := 2*root + 1 // 超出切片,则break if child >= hi { break } // child+1就是右孩子节点 // 不停的移动child值,让child位置的值最大 if child+1 < hi && data.Less(first+child, first+child+1) { child++ } // 如果root位置的值就是最大值,则直接返回 if !data.Less(first+root, first+child) { return } // 交换root值和孩子值,让root的值保持最大 data.Swap(first+root, first+child) root = child } }
// 堆排序 funcheapSort(data Interface, a, b int) { first := a lo := 0 hi := b - a
// Build heapheap with greatest element at top. // 构建最大数在顶端的堆 for i := (hi - 1) / 2; i >= 0; i-- { siftDown(data, i, hi, first) }
// Pop elements, largest first, into end of data. // 丢出元素,先丢出最大元素,放在切片末尾 // 并且维护堆属性,继续选出最大堆 for i := hi - 1; i >= 0; i-- { data.Swap(first, first+i) siftDown(data, lo, i, first) } }
插入排序
1 2 3 4 5 6 7 8 9
// insertionSort sorts data[a:b] using insertion sort. // 插入排序,将后面的数,小于前面的数时,进行调换 funcinsertionSort(data Interface, a, b int) { for i := a + 1; i < b; i++ { for j := i; j > a && data.Less(j, j-1); j-- { data.Swap(j, j-1) } } }
通过支点切分数组
先取支点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// 将数组中的三个角标的数字中的中间大小的数移动到中间,也就是选择一个不是最大也不是最小的数作为支点,放在首位。 // medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1]. funcmedianOfThree(data Interface, m1, m0, m2 int) { // sort 3 elements if data.Less(m1, m0) { data.Swap(m1, m0) } // data[m0] <= data[m1] if data.Less(m2, m1) { data.Swap(m2, m1) // data[m0] <= data[m2] && data[m1] < data[m2] if data.Less(m1, m0) { data.Swap(m1, m0) } } // now data[m0] <= data[m1] <= data[m2] }
// doPivot 切分切片,切成两个等分,并且第一等分中的数字都小于第二等分 // 影响快排的速度就是这个支点的位置,如果支点位置好,则区分就快 funcdoPivot(data Interface, lo, hi int) (midlo, midhi int) { // 取平均数,避免整数溢出(int最大数小于uint最大数,int有一位存储符号) m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow. // 取元素中的头、中、尾三个位置的元素的中值元素移动到中间位置 // 当元素个数大于40,则将数据分为三个部分,分别将三个部分的三元素中中值元素移动到中间位置 if hi-lo > 40 { // Tukey's ``Ninther,'' median of three medians of three. s := (hi - lo) / 8 medianOfThree(data, lo, lo+s, lo+2*s) medianOfThree(data, m, m-s, m+s) medianOfThree(data, hi-1, hi-1-s, hi-1-2*s) } medianOfThree(data, lo, m, hi-1) // 现在变成 data[lo] <= data[m] <= data[hi-1]
// Invariants are: // data[lo] = pivot (set up by ChoosePivot) // data[lo < i < a] < pivot // data[a <= i < b] <= pivot // data[b <= i < c] unexamined // data[c <= i < hi-1] > pivot // data[hi-1] >= pivot // 选择起始位置作为支点 pivot := lo a, c := lo+1, hi-1
// 下面的逻辑是使用支点排序,小于支点的往前移动,大于支点的往后移动 // 找到第一个大于支点的角标 for ; a < c && data.Less(a, pivot); a++ { } b := a for { // 找到 大于支点之后 又小于支点的角标 for ; b < c && !data.Less(pivot, b); b++ { // data[b] <= pivot } // 从后往前,找到大于支点的角标 for ; b < c && data.Less(pivot, c-1); c-- { // data[c-1] > pivot } if b >= c { break } // 交换两个角标, // data[b] > pivot; data[c-1] <= pivot data.Swap(b, c-1) b++ c-- } // 检测是否有重复元素,如果有重复元素, // If hi-c<3 then there are duplicates (by property of median of nine). // Let's be a bit more conservative, and set border to 5. protect := hi-c < 5 if !protect && hi-c < (hi-lo)/4 { // Lets test some points for equality to pivot // 检查与支点重复的元素个数 dups := 0 if !data.Less(pivot, hi-1) { // data[hi-1] = pivot data.Swap(c, hi-1) c++ dups++ } if !data.Less(b-1, pivot) { // data[b-1] = pivot b-- dups++ } // m-lo = (hi-lo)/2 > 6 // b-lo > (hi-lo)*3/4-1 > 8 // ==> m < b ==> data[m] <= pivot if !data.Less(m, pivot) { // data[m] = pivot data.Swap(m, b-1) b-- dups++ } // if at least 2 points are equal to pivot, assume skewed distribution protect = dups > 1 } if protect { // Protect against a lot of duplicates // Add invariant: // data[a <= i < b] unexamined // data[b <= i < c] = pivot for { for ; a < b && !data.Less(b-1, pivot); b-- { // data[b] == pivot } for ; a < b && data.Less(a, pivot); a++ { // data[a] < pivot } if a >= b { break } // data[a] == pivot; data[b-1] < pivot data.Swap(a, b-1) a++ b-- } } // Swap pivot into middle data.Swap(pivot, b-1) return b - 1, c }
// 快速排序(核心逻辑并不完全是快排) funcquickSort(data Interface, a, b, maxDepth int) { // 当元素个数大于12个时,使用快排 for b-a > 12 { if maxDepth == 0 { // 深度为0,使用堆排序 heapSort(data, a, b) return } maxDepth-- // 当元素大于12个,将元素拆开成2个部分,将大问题划分为小问题 mlo, mhi := doPivot(data, a, b) // Avoiding recursion on the larger subproblem guarantees // a stack depth of at most lg(b-a). if mlo-a < b-mhi { quickSort(data, a, mlo, maxDepth) a = mhi // i.e., quickSort(data, mhi, b) } else { quickSort(data, mhi, b, maxDepth) b = mlo // i.e., quickSort(data, a, mlo) } } // Use ShellSort for slices <= 12 elements 当元素小于12个时,使用希尔排序 if b-a > 1 { // Do ShellSort pass with gap 6 // It could be written in this simplified form cause b-a <= 12 // 在元素个数小于12时,希尔排序可以简化成这个样子 // 插入排序之前,先间隔六个的数字比较一下 for i := a + 6; i < b; i++ { if data.Less(i, i-6) { data.Swap(i, i-6) } } // 使用插入排序 insertionSort(data, a, b) } }
iflen(s) <= 1 { return s } left := make([]int, 0) right := make([]int, 0) mid := make([]int, 0) res := make([]int, 0) flag := s[0] mid = append(mid, flag) for _, v := range s { if v > flag { right = append(right, v) } elseif v < flag { left = append(left, v) } } left, right = kuaipai(left), kuaipai(right) res = append(append(left, mid...), right...) return res