1. 程式人生 > 其它 >golang int切片排序_Golang之 sort 排序原始碼淺析

golang int切片排序_Golang之 sort 排序原始碼淺析

技術標籤:golang int切片排序

點選上方藍色 “鐵匠學程式設計” 關注我,讓我們一起學習!

需求提測了,不想摸魚度日(希望不要被同事看到?),看了下sort包,今天給大家簡單分享下。

sort包的使用

在Golang中引入排序是在sort包下面,使用方法是:

package mainimport"sort"funcmain() {sort.Sort(data)}

sort.Interface 介面方法的定義

// An implementation of Interface can be sorted by the routines in this package.// The methods refer to elements of the underlying collection by integer index.type Interface interface {  // Len is the number of elements in the collection.  Len() int  // Less reports whether the element with index i//mustsortbeforetheelementwithindexj.  Less(i, j int) bool  // Swap swaps the elements with indexes i and j.  Swap(i, j int)}

排序演算法

sort包實現了四種基本排序演算法:插入排序、歸併排序、堆排序和快速排序。這四種排序方法不是公開的只能在sort包內部使用。sort包提供了對 切片、[]float64切片和 []string 切片的支援。

通過 do doc 可以看到sort包含的方法:

package sort // import "sort"Package sort provides primitives for sorting slices and user-definedcollections.func Float64s(a []float64)func Float64sAreSorted(a []float64) boolfunc Ints(a []int)func IntsAreSorted(a []int) boolfunc IsSorted(data Interface) boolfunc Search(n int, f func(int) bool) intfunc SearchFloat64s(a []float64, x float64) intfunc SearchInts(a []int, x int) intfunc SearchStrings(a []string, x string) intfunc Slice(slice interface{}, less func(i, j int) bool)func SliceIsSorted(slice interface{}, less func(i, j int) bool) boolfunc SliceStable(slice interface{}, less func(i, j int) bool)func Sort(data Interface)func Stable(data Interface)func Strings(a []string)func StringsAreSorted(a []string) booltype Float64Slice []float64type IntSlice []inttype Interface interface{ ... }funcReverse(dataInterface)Interfacetype StringSlice []string

直搗龍穴看原始碼

// Sort sorts data.// It makes one call to data.Len to determine n and O(n*log(n)) calls to//data.Lessanddata.Swap.Thesortisnotguaranteedtobestable.func Sort(data Interface) {  n := data.Len()  quickSort(data, 0, n, maxDepth(n))}

註釋中最後也說明了不能保證排序的穩定性,也就是說對於相同的元素並不能保證排序前後的順序一致。

看到 quickSort 之後我認為就使用了快速排序,其實不然!在這之前先看下 maxDepth() 函式,這個函式是幹嘛的?

// maxDepth returns a threshold at which quicksort should switch// to heapsort. It returns 2*ceil(lg(n+1)).func maxDepth(n int) int {  var depth int  for i := n; i > 0; i >>= 1 {    depth++  }  return depth * 2}

這個是判斷是否可以使用 堆排序。這個depth 值用了決定是否採用堆排序。

快速排序quickSort()函式

func quickSort(data Interface, a, b, maxDepth int) {  for b-a > 12 { // Use ShellSort for slices <= 12 elements    if maxDepth == 0 {      heapSort(data, a, b)      return    }    maxDepth--    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)    }  }  if b-a > 1 {    // Do ShellSort pass with gap 6    // It could be written in this simplified form cause b-a <= 12...    insertionSort(data, a, b)  }}

sort函式使用的 quickSort 分為兩部分,當長度大於12的時候:首先在maxDepth為0的情況下,使用堆排序,就是當遞迴到最大深度的時候,使用堆排序。那麼在不為零的時候我們可以看出使用的就是快速排序,不過在快速排序中,又進行了一步優化,也就是找中位數 doPivot() 這個方法。

當長度小於等於12的時候使用 希爾排序,我們就先看下希爾排序。

插入排序

Golang中的希爾排序中使用的是Gap值是6,也就是間隔6位為一組,先進行排序,然後不同於一般的希爾排序將Gap值減半,而是直接進行插入排序。

前面的文章也給大家分享過插入排序,他的核心思想在於將未排序的元素在已排序的區間中找到合適的位置進行插入。

// insertionSort sorts data[a:b] using insertion sort.func insertionSort(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)    }  }}

尋找中位數

快速排序的關鍵就是找到一個合適的臨界值,合適的臨界值對排序很關鍵,好的中位數可以將元素平均的分為兩部分,整體分割的此時明顯會減少,最後能夠降低耗費的時間。

func doPivot(data Interface, lo, hi int) (midlo, midhi int) {  m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow.  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)  // 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}

sort包當前支援的內部資料型別排序

IntSlice 及 []int 排序

// IntSlice attaches the methods of Interface to []int, sorting in increasing order.type IntSlice []intfunc (p IntSlice) Len() int           { return len(p) }func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }func (p IntSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }// Sort is a convenience method.func (p IntSlice) Sort() { Sort(p) }

降序排序就用到了reverse()方法:

// Reverse returns the reverse order for data.func Reverse(data Interface) Interface {  return &reverse{data}}

Float64Slice型別及[]float64排序

// Float64Slice attaches the methods of Interface to []float64, sorting in increasing order// (not-a-number values are treated as less than other values).type Float64Slice []float64func (p Float64Slice) Len() int           { return len(p) }func (p Float64Slice) Less(i, j int) bool { return p[i] < p[j] || isNaN(p[i]) && !isNaN(p[j]) }func (p Float64Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }// isNaN is a copy of math.IsNaN to avoid a dependency on the math package.func isNaN(f float64) bool {  return f != f}

StringSlice型別及[]string排序

// StringSlice attaches the methods of Interface to []string, sorting in increasing order.type StringSlice []stringfunc (p StringSlice) Len() int           { return len(p) }func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] }func (p StringSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }// Sort is a convenience method.func (p StringSlice) Sort() { Sort(p) }

學習排序原始碼受益匪淺,要學習和進步的空間太大了!邊界條件是程式碼安全的最大保障!

歡迎來噴~

鼓勵下加個關注吧!

390926fa4f6ed1a7cdbec73e48680c4c.png

加油!不僅自己~還有你~