Go 語言標準庫之 sort 包
sort 包中實現了 5 種基本的排序演算法:插入排序、希爾排序、堆排序、快速排序和歸併排序,和其他語言中一樣,這 5 種演算法都是不公開的,它們只在 sort 包內部使用。
sort.Interface 介面
實現了sort.interface
的型別(一般為集合),都可以使用 sort 包中函式進行排序 ,sort.Interface
介面定義如下:
type Interface interface { // Len 方法返回集合中的元素個數 Len() int // Less 方法報告索引 i 的元素是否比索引 j 的元素小 Less(i, j int) bool // Swap 方法交換索引 i 和 j 的兩個元素 Swap(i, j int) }
自定義型別排序
Sort/Stable 函式
// 對 data 進行排序,不保證穩定性,即相等元素的相對次序可能改變 // Sort 呼叫 1 次 data.Len 確定長度,呼叫 O(n*log(n)) 次 data.Less 和 data.Swap func Sort(data Interface) // 對 data 進行排序,保證排序的穩定性,即相等元素的相對次序不變 // Sort 呼叫 1 次 data.Len,O(n*log(n)) 次 data.Less 和 O(n*log(n)*log(n)) 次 data.Swap func Stable(data Interface) // 包裝一個 sort.Interface 介面並返回一個新的 sort.Interface 介面,對該介面排序可生成遞減序列 func Reverse(data Interface) Interface // 判斷 data 是否已經被排序 func IsSorted(data Interface) bool
示例程式碼如下:
type Student struct { Name string Age int } type StudentSlice []Student // 實現 sort.Interface 介面 func (ss StudentSlice) Len() int { return len(ss) } func (ss StudentSlice) Less(i, j int) bool { return ss[i].Age < ss[j].Age } func (ss StudentSlice) Swap(i, j int) { ss[i], ss[j] = ss[j], ss[i] } func main() { ss := StudentSlice{ {"Alice", 18}, {"Bob", 20}, {"Allen", 16}, {"Michelle", 18}, {"James", 24}, } // 按照年齡升序排列 sort.Sort(ss) fmt.Println(ss) // [{Allen 16} {Alice 18} {Michelle 18} {Bob 20} {James 24}] fmt.Println(sort.IsSorted(ss)) // true // 按照年齡降序排列 sort.Sort(sort.Reverse(ss)) fmt.Println(ss) // [{James 24} {Bob 20} {Alice 18} {Michelle 18} {Allen 16}] fmt.Println(sort.IsSorted(ss)) // false }
Slice/SliceStable 函式
在前面Sort/Stable
函式的排序中,自定義型別的切片都需要實現sort.Interface
介面,在程式碼上不太簡潔。Go 提供Slice/SliceStable
函式只需傳入一個待排序的切片,一個回撥函式即可進行排序。需要注意,除了下列三個函式,sort 包下其它所有函式的入參物件x interface{}
都需要實現sort.Interface
介面,原因後文會分析。
// 使用 less 函式定義的規則對切片 x 進行排序,不保證排序的穩定性,x 不是切片會報 panic
func Slice(x interface{}, less func(i, j int) bool)
// 使用 less 函式定義的規則對切片 x 進行排序,保證排序的穩定性,x 不是切片會報 panic
func SliceStable(x interface{}, less func(i, j int) bool)
// 判斷切片 x 根據 less 函式定義的規則是否是有序的,x 不是切片會報 panic
func SliceIsSorted(x interface{}, less func(i, j int) bool)
示例程式碼如下:
type Student struct {
Name string
Age int
}
func main() {
ss := []Student{
{"Alice", 18},
{"Bob", 20},
{"Allen", 16},
{"Michelle", 18},
{"James", 24},
}
less := func(i, j int) bool {
return ss[i].Age < ss[j].Age
}
// 按照年齡升序排列
sort.Slice(ss, less)
fmt.Println(ss) // [{Allen 16} {Alice 18} {Michelle 18} {Bob 20} {James 24}]
fmt.Println(sort.SliceIsSorted(ss, less)) // true
}
基本資料型別排序
對於 int、float64 和 string 型別的切片,Go 語言進行了封裝,可以直接呼叫 sort 包函式進行排序。
int 型別排序
// 在 sort 包下,Go 語言定義了 IntSlice 型別實現 sort.Interface 介面,用於 []int 排序
type IntSlice []int
// 實現 Interface 介面
func (x IntSlice) Len() int { return len(x) }
func (x IntSlice) Less(i, j int) bool { return x[i] < x[j] }
func (x IntSlice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
// 等價呼叫 sort.Sort(x)
func (x IntSlice) Sort() { Sort(x) }
// 等價呼叫 sort.SearchInts(x, p)
func (x IntSlice) Search(p int) int { return SearchInts(x, p) }
// 對 x 進行升序排序
func Ints(x []int)
// 判斷 x 是否為升序
func IntsAreSorted(x []int)
// 在升序順序的 a 中搜索 x,返回 x 的索引
// 如果查詢不到,返回值是 x 應該插入 a 的位置(以保證 a 的遞增順序),返回值可以是 len(a)
func SearchInts(a []int, x int) int
示例程式碼如下:
func main() {
// 升序方式一:
intList1 := []int{2, 0, 1, 8, 0, 9, 2, 4, 1, 2}
sort.Ints(intList1)
fmt.Println(intList1) // [0 0 1 1 2 2 2 4 8 9]
// 升序方式二:
intList2 := []int{2, 0, 1, 8, 0, 9, 2, 4, 1, 2}
sort.Sort(sort.IntSlice(intList2))
fmt.Println(intList2) // [0 0 1 1 2 2 2 4 8 9]
// 升序方法三:
intList3 := []int{2, 0, 1, 8, 0, 9, 2, 4, 1, 2}
(sort.IntSlice(intList3)).Sort()
fmt.Println(intList3) // [0 0 1 1 2 2 2 4 8 9]
// 降序方式:
intList4 := []int{2, 0, 1, 8, 0, 9, 2, 4, 1, 2}
sort.Sort(sort.Reverse(sort.IntSlice(intList4)))
fmt.Println(intList4) // [9 8 4 2 2 2 1 1 0 0]
intList5 := []int{0, 0, 1, 1, 2, 2, 2, 4, 8, 9}
// 搜尋方式一:
fmt.Println(sort.SearchInts(intList5, 2)) // 4
// 搜尋方式二:
fmt.Println(sort.IntSlice(intList5).Search(2)) // 4
}
float64 型別排序
// 在 sort 包下,Go 語言定義了 Float64Slice 型別實現 sort.Interface 介面,用於 []float64 排序
type Float64Slice []float64
// 實現 Interface 介面
func (x Float64Slice) Len() int { return len(x) }
func (x Float64Slice) Less(i, j int) bool { return x[i] < x[j] || (isNaN(x[i]) && !isNaN(x[j])) }
func (x Float64Slice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
// 等價呼叫 sort.Sort(x)
func (x Float64Slice) Sort() { Sort(x) }
// 等價呼叫 sort.SearchFloat64s(x, p)
func (x Float64Slice) Search(p float64) int { return SearchFloat64s(x, p) }
// 對 x 進行升序排序
func Float64s(x []float64)
// 判斷 x 是否為升序
func Float64sAreSorted(x []float64)
// 在升序順序的 a 中搜索 x,返回 x 的索引
// 如果查詢不到,返回值是 x 應該插入 a 的位置(以保證 a 的遞增順序),返回值可以是 len(a)
func SearchFloat64s(a []float64, x float64) int
示例程式碼如下:
func main() {
// 升序方式一:
floatList1 := []float64{4.2, 5.9, 12.3, 10.0, 50.4, 99.9, 31.4, 27.81828, 3.14}
sort.Float64s(floatList1)
fmt.Println(floatList1) // [3.14 4.2 5.9 10 12.3 27.81828 31.4 50.4 99.9]
// 升序方式二:
floatList2 := []float64{4.2, 5.9, 12.3, 10.0, 50.4, 99.9, 31.4, 27.81828, 3.14}
sort.Sort(sort.Float64Slice(floatList2))
fmt.Println(floatList2) // [3.14 4.2 5.9 10 12.3 27.81828 31.4 50.4 99.9]
// 升序方法三:
floatList3 := []float64{4.2, 5.9, 12.3, 10.0, 50.4, 99.9, 31.4, 27.81828, 3.14}
(sort.Float64Slice(floatList3)).Sort()
fmt.Println(floatList3) // [3.14 4.2 5.9 10 12.3 27.81828 31.4 50.4 99.9]
// 降序方式:
floatList4 := []float64{4.2, 5.9, 12.3, 10.0, 50.4, 99.9, 31.4, 27.81828, 3.14}
sort.Sort(sort.Reverse(sort.Float64Slice(floatList4)))
fmt.Println(floatList4) // [99.9 50.4 31.4 27.81828 12.3 10 5.9 4.2 3.14]
floatList5 := []float64{3.14, 4.2, 5.9, 10, 12.3, 27.81828, 31.4, 50.4, 99.9}
// 搜尋方式一:
fmt.Println(sort.SearchFloat64s(floatList5, 12.3)) // 4
// 搜尋方式二:
fmt.Println(sort.Float64Slice(floatList5).Search(12.3)) // 4
}
string 型別排序
// 在 sort 包下,Go 語言定義了 StringSlice 型別實現 sort.Interface 介面,用於 []string 排序
type StringSlice []string
// 實現 Interface 介面
func (x StringSlice) Len() int { return len(x) }
func (x StringSlice) Less(i, j int) bool { return x[i] < x[j] }
func (x StringSlice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
// 等價呼叫 sort.Sort(x)
func (x StringSlice) Sort() { Sort(x) }
// 等價呼叫 sort.SearchStrings(x, p)
func (x StringSlice) Search(p string) int { return SearchStrings(x, p) }
// 對 x 進行升序排序
func Strings(x []string)
// 判斷 x 是否為升序
func StringsAreSorted(x []string)
// 在升序順序的 a 中搜索 x,返回 x 的索引
// 如果查詢不到,返回值是 x 應該插入 a 的位置(以保證 a 的遞增順序),返回值可以是 len(a)
func SearchStrings(a []string, x string) int
示例程式碼如下:
func main() {
// 升序方式一:
stringList1 := []string{"o", "l", "d", "b", "o", "y", "g", "o", "l", "a", "n", "g"}
sort.Strings(stringList1)
fmt.Printf("%q\n", stringList1) // ["a" "b" "d" "g" "g" "l" "l" "n" "o" "o" "o" "y"]
// 升序方式二:
stringList2 := []string{"o", "l", "d", "b", "o", "y", "g", "o", "l", "a", "n", "g"}
sort.Sort(sort.StringSlice(stringList2))
fmt.Printf("%q\n", stringList2) // ["a" "b" "d" "g" "g" "l" "l" "n" "o" "o" "o" "y"]
// 升序方式三:
stringList3 := []string{"o", "l", "d", "b", "o", "y", "g", "o", "l", "a", "n", "g"}
(sort.StringSlice(stringList3)).Sort()
fmt.Printf("%q\n", stringList3) // ["a" "b" "d" "g" "g" "l" "l" "n" "o" "o" "o" "y"]
// 降序方式:
stringList4 := []string{"o", "l", "d", "b", "o", "y", "g", "o", "l", "a", "n", "g"}
sort.Sort(sort.Reverse(sort.StringSlice(stringList4)))
fmt.Printf("%q\n", stringList4) // ["y" "o" "o" "o" "n" "l" "l" "g" "g" "d" "b" "a"]
stringList5 := []string{"a", "b", "d", "g", "g", "l", "l", "n", "o", "o", "o", "y"}
// 搜尋方式一:
fmt.Println(sort.SearchStrings(stringList5, "l")) // 5
// 搜尋方式二:
fmt.Println((sort.StringSlice(stringList5)).Search("l")) // 5
}
底層實現分析
sort.Sort 函式
// Sort 函式是一個不穩定方式排序,使用希爾快速、快速排序或者堆排序三種排序方式之一
func Sort(data Interface) {
n := data.Len()
// maxDepth(n) 返回 2*ceil(lg(n+1)),元素深度達到 2*ceil(lg(n+1)) 則選用堆排序
quickSort(data, 0, n, maxDepth(n))
}
// quickSort 會根據與元素深度自動選擇使用希爾快速、快速排序或者堆排序三種排序方式之一
func quickSort(data Interface, a, b, maxDepth int) {
for b-a > 12 {
if maxDepth == 0 { // Use ShellSort for slices <= 12 elements
// 使用堆排序
heapSort(data, a, b)
return
}
maxDepth--
// 使用三向切分快速排序,通過 doPivot 進行快排的分割槽
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)
}
}
// 切片元素小於等於 12 個,使用希爾排序(希爾排序是插入排序的高效版本),間隔 d=6
if b-a > 1 {
// Do ShellSort pass with gap 6
// It could be written in this simplified form cause b-a <= 12
for i := a + 6; i < b; i++ {
if data.Less(i, i-6) {
data.Swap(i, i-6)
}
}
insertionSort(data, a, b)
}
}
// 堆排序
func heapSort(data Interface, a, b int) {
first := a
lo := 0
hi := b - a
// Build heap 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)
}
}
// siftDown implements the heap property on data[lo:hi].
// first is an offset into the array where the root of the heap lies.
func siftDown(data Interface, lo, hi, first int) {
root := lo
for {
child := 2*root + 1
if child >= hi {
break
}
if child+1 < hi && data.Less(first+child, first+child+1) {
child++
}
if !data.Less(first+root, first+child) {
return
}
data.Swap(first+root, first+child)
root = child
}
}
// 插入排序
// 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)
}
}
}
sort.Stable 函式
// Stable 是一個穩定方式的排序,使用歸併排序
func Stable(data Interface) {
stable(data, data.Len())
}
// 這裡用到的歸併排序演算法是一種原址排序演算法:
// 首先,它把 slice 按照每 blockSize=20 個元素為一個 slice,進行插入排序
// 迴圈合併相鄰的兩個 block,每次迴圈 blockSize 擴大二倍,直到 blockSize > n 為止
func stable(data Interface, n int) {
// 初始 blockSize 設定為 20
blockSize := 20 // must be > 0
a, b := 0, blockSize
// 對每個塊(以及剩餘不足blockSize的一個塊)進行插入排序
for b <= n {
insertionSort(data, a, b)
a = b
b += blockSize
}
insertionSort(data, a, n)
for blockSize < n {
a, b = 0, 2*blockSize
for b <= n {
// 每兩個 blockSize 進行合併
symMerge(data, a, a+blockSize, b)
a = b
b += 2 * blockSize
}
// 剩餘一個多 blockSize 進行合併
if m := a + blockSize; m < n {
symMerge(data, a, m, n)
}
blockSize *= 2
}
}
func symMerge(data Interface, a, m, b int) {
// Avoid unnecessary recursions of symMerge
// by direct insertion of data[a] into data[m:b]
// if data[a:m] only contains one element.
if m-a == 1 {
// Use binary search to find the lowest index i
// such that data[i] >= data[a] for m <= i < b.
// Exit the search loop with i == b in case no such index exists.
i := m
j := b
for i < j {
h := int(uint(i+j) >> 1)
if data.Less(h, a) {
i = h + 1
} else {
j = h
}
}
// Swap values until data[a] reaches the position before i.
for k := a; k < i-1; k++ {
data.Swap(k, k+1)
}
return
}
// Avoid unnecessary recursions of symMerge
// by direct insertion of data[m] into data[a:m]
// if data[m:b] only contains one element.
if b-m == 1 {
// Use binary search to find the lowest index i
// such that data[i] > data[m] for a <= i < m.
// Exit the search loop with i == m in case no such index exists.
i := a
j := m
for i < j {
h := int(uint(i+j) >> 1)
if !data.Less(m, h) {
i = h + 1
} else {
j = h
}
}
// Swap values until data[m] reaches the position i.
for k := m; k > i; k-- {
data.Swap(k, k-1)
}
return
}
mid := int(uint(a+b) >> 1)
n := mid + m
var start, r int
if m > mid {
start = n - b
r = mid
} else {
start = a
r = m
}
p := n - 1
for start < r {
c := int(uint(start+r) >> 1)
if !data.Less(p-c, c) {
start = c + 1
} else {
r = c
}
}
end := n - start
if start < m && m < end {
rotate(data, start, m, end)
}
if a < start && start < mid {
symMerge(data, a, start, mid)
}
if mid < end && end < b {
symMerge(data, mid, end, b)
}
}
sort.Slice 函式
// Slice 函式是一個不穩定方式排序,使用希爾快速、快速排序或者堆排序三種排序方式之一
// Slice sorts the slice x given the provided less function.
// It panics if x is not a slice.
//
// The sort is not guaranteed to be stable: equal elements
// may be reversed from their original order.
// For a stable sort, use SliceStable.
//
// The less function must satisfy the same requirements as
// the Interface type's Less method.
func Slice(x interface{}, less func(i, j int) bool) {
rv := reflectValueOf(x)
swap := reflectSwapper(x)
length := rv.Len()
// maxDepth(n) 返回 2*ceil(lg(n+1)),元素深度達到 2*ceil(lg(n+1)) 則選用堆排序
quickSort_func(lessSwap{less, swap}, 0, length, maxDepth(length))
}
// lessSwap is a pair of Less and Swap function for use with the
// auto-generated func-optimized variant of sort.go in
// zfuncversion.go.
type lessSwap struct {
Less func(i, j int) bool
Swap func(i, j int)
}
// quickSort_func 會根據與元素深度自動選擇使用希爾快速、快速排序或者堆排序三種排序方式之一
// Auto-generated variant of sort.go:quickSort
func quickSort_func(data lessSwap, a, b, maxDepth int) {
for b-a > 12 {
if maxDepth == 0 {
// 使用堆排序
heapSort_func(data, a, b)
return
}
maxDepth--
// 使用三向切分快速排序,通過 doPivot_func 進行快排的分割槽
mlo, mhi := doPivot_func(data, a, b)
if mlo-a < b-mhi {
quickSort_func(data, a, mlo, maxDepth)
a = mhi
} else {
quickSort_func(data, mhi, b, maxDepth)
b = mlo
}
}
// 切片元素小於等於 12 個,使用希爾排序(希爾排序是插入排序的高效版本),間隔 d=6
if b-a > 1 {
for i := a + 6; i < b; i++ {
if data.Less(i, i-6) {
data.Swap(i, i-6)
}
}
insertionSort_func(data, a, b)
}
}
從上面可以看到,sort.Slice
呼叫的排序演算法函式quickSort_func
的入參是data lessSwap
,而不是data Interface
,呼叫的插入排序、堆排序、快速排序函式也和sort.Sort
不同。sort.Slice
和sort.Sort
的實現原理是相同的,區別在於sort.Sort
通過傳入實現sort.Interface
介面的物件獲得Len()/Swap()/Less()
,sort.Slice
則是通過反射的方式獲取Len()
和Swap()
,Less()
通過傳參獲得。因此,sort.Slice
傳入的物件不需要實現sort.Interface
介面。
sort.SliceStable 函式
// SliceStable 是一個穩定方式的排序,使用歸併排序
// SliceStable sorts the slice x using the provided less
// function, keeping equal elements in their original order.
// It panics if x is not a slice.
//
// The less function must satisfy the same requirements as
// the Interface type's Less method.
func SliceStable(x interface{}, less func(i, j int) bool) {
rv := reflectValueOf(x)
swap := reflectSwapper(x)
stable_func(lessSwap{less, swap}, rv.Len())
}
// lessSwap is a pair of Less and Swap function for use with the
// auto-generated func-optimized variant of sort.go in
// zfuncversion.go.
type lessSwap struct {
Less func(i, j int) bool
Swap func(i, j int)
}
// 這裡用到的歸併排序演算法是一種原址排序演算法:
// 首先,它把 slice 按照每 blockSize=20 個元素為一個 slice,進行插入排序
// 迴圈合併相鄰的兩個 block,每次迴圈 blockSize 擴大二倍,直到 blockSize > n 為止
// Auto-generated variant of sort.go:stable
func stable_func(data lessSwap, n int) {
blockSize := 20 // 初始 blockSize 設定為 20
a, b := 0, blockSize
// 對每個塊(以及剩餘不足blockSize的一個塊)進行插入排序
for b <= n {
insertionSort_func(data, a, b)
a = b
b += blockSize
}
insertionSort_func(data, a, n)
for blockSize < n {
a, b = 0, 2*blockSize
for b <= n {
// 每兩個 blockSize 進行合併
symMerge_func(data, a, a+blockSize, b)
a = b
b += 2 * blockSize
}
// 剩餘一個多 blockSize 進行合併
if m := a + blockSize; m < n {
symMerge_func(data, a, m, n)
}
blockSize *= 2
}
}
和sort.Slice
一樣,sort.SliceStable
函式的入參是data lessSwap
,不是data Interface
,通過反射的方式獲取Len()
和Swap()
,Less()
通過傳參獲得。