Golang標準庫:sort排序
sort包中實現了3種基本的排序演算法:插入排序.快排和堆排序,但是這三種方式都只在sort包內部使用,所以使用者在使用sort包進行排序時無需考慮使用那種排序方式,sort包會根據實際資料自動選擇高效的排序演算法。
基本方法
排序(介面)的三個要素:
- 待排序元素個數 n ;
- 第 i 和第 j 個元素的比較函式 cmp ;
- 第 i 和 第 j 個元素的交換 swap ;
sort 包中有一個 sort.Interface 介面,該介面有三個方法Len()
、Less(i,j int)
和Swap(i,j int)
,如下:
type Interface interface { // Len方法返回集合中的元素個數 Len() int // Less方法報告索引i的元素是否比索引j的元素小 Less(i, j int) bool // Swap方法交換索引i和j的兩個元素 Swap(i, j int) }
任何實現了sort.Interface
的型別(一般為集合),均可呼叫該包的Sort()
方法進行排序。這些方法要求集合內列出元素的索引為整數。通用排序函式sort.Sort
可以排序任何實現了sort.Inferface
介面的物件(變數)。方法定義如下:
func Sort(data Interface)# 排序data,不保證穩定性 func Stable(data Interface)# 排序data,保證穩定性 func Reverse(data Interface) Interface# Reverse包裝一個Interface介面並返回一個新的Interface介面,對該介面排序可生成遞減序列 func IsSorted(data Interface) bool# 報告data是否已經被排序 func Search(nint, ffunc(int) bool) int# 採用二分法搜尋找到[0, n)區間內最小的滿足f(i)==true的值i
基本型別排序
sort
包原生支援[]int
、[]float64
和[]string
三種內建資料型別切片的排序操作,不僅提供了一些特殊的方法,還針對每種型別實現了相應的sort.Interface
介面,即不必我們自己實現相關的Len()
、Less()
和Swap()
方法。
整型切片排序
sort
包提供了針對整型切片的如下方法:
func Ints(a []int)# 將a排序為遞增順序 func IntsAreSorted(a []int) bool# 判斷 a 是否已排序為遞增順序。 func SearchInts(a []int, xint) int# 在遞增順序的 a 中搜索 x,返回 x 的索引。如果查詢不到,返回值是 x 應該插入 a 的位置(以保證 a 的遞增順序),返回值可以是len(a)。
排序示例:
s := []int{5, 2, 6, 3, 1, 4} fmt.Println("是否已排序:", sort.IntsAreSorted(s)) sort.Ints(s) fmt.Println("元素 3 的升序排序後索引為:", sort.SearchInts(s, 3)) fmt.Println("是否已排序:", sort.IntsAreSorted(s)) fmt.Println(s)
輸出結果:
是否已排序: false 元素 1 的升序排序後索引為: 2 是否已排序: true [1 2 3 4 5 6]
除了使用特殊指定的函式外,還可以使用改裝過的型別IntSclice
型別, 然後直接呼叫它們對應的Sort()
方法,因為這種型別也實現了sort.Interface
介面:
type IntSlice []int - func (p IntSlice)Len()int - func (p IntSlice) Less(i, jint) bool - func (p IntSlice) Search(xint) int - func (p IntSlice) Sort() - func (p IntSlice) Swap(i, jint)
排序示例:
s := sort.IntSlice{5, 2, 6, 3, 1, 4} s.Sort() fmt.Println(s)
輸出結果:
[1 2 3 4 5 6]
浮點型切片排序
與整型切片排序相同,sort
包提供了針對浮點型切片的如下方法:
func Float64s(a []float64) func Float64sAreSorted(a []float64) bool func SearchFloat64s(a []float64, xfloat64) int
同樣,也可以使用實現了sort.Interface
介面的Float64Slice
型別,並實現了其中的方法:
type Float64Slice []float64 - func (p Float64Slice)Len()int - func (p Float64Slice) Less(i, jint) bool - func (p Float64Slice) Search(xfloat64) int - func (p Float64Slice) Sort() - func (p Float64Slice) Swap(i, jint)
字串切片排序
與整型切片排序相同,sort
包提供了針對字串切片的如下方法:
func Strings(a []string) func StringsAreSorted(a []string) bool func SearchStrings(a []string, xstring) int
同樣,也可以使用實現了sort.Interface
介面的StringSlice
型別,並實現了其中的方法:
type StringSlice []string - func (p Float64Slice)Len()int - func (p Float64Slice) Less(i, jint) bool - func (p Float64Slice) Search(xstring) int - func (p Float64Slice) Sort() - func (p Float64Slice) Swap(i, jint)
穩定排序
Sort
排序data
,它呼叫 1 次data.Len
確定長度,呼叫 O(n*log(n)) 次data.Less
和data.Swap
。Sort()
函式不能保證排序的穩定性(即不保證相等元素的相對次序不變)。
而Stable()
函式排序data
則可以保證排序的穩定性,相等元素的相對次序不變。它呼叫 1 次data.Len
,O(n*log(n)) 次data.Less
和 O(n*log(n)*log(n)) 次data.Swap
。
func Stable(data Interface)
降序排列
sort
包可以使用sort.Reverse(slice)
來調換slice.Interface.Less
,也就是比較函式,所以,int
、float64
和string
的逆序排序函式可以使用Reverse
方法來實現。排序示例:
s := sort.IntSlice{5, 2, 6, 3, 1, 4} sort.Sort(sort.Reverse(s)) fmt.Println(s)
輸出結果:
[6 5 4 3 2 1]
排序搜尋
Search
函式採用二分法搜尋找到 [0, n) 區間內最小的滿足f(i)==true
的值 i 。也就是說,Search
函式希望 f 在輸入位於區間 [0, n) 的前面某部分(可以為空)時返回假,而在輸入位於剩餘至結尾的部分(可以為空)時返回真;Search
函式會返回滿足f(i)==true
的最小值 i 。如果沒有該值,函式會返回 n。注意,未找到時的返回值不是 -1,這一點和strings.Index
等函式不同。Search
函式只會用區間[0, n)內的值呼叫 f。
x := 11 s := []int{3, 6, 8, 11, 45} //注意:已經升序排序 pos := sort.Search(len(s), func(iint)bool { return s[i] >= x }) if pos < len(s) && s[pos] == x { fmt.Println(x, "在s中的位置為:", pos) } else { fmt.Println("s不包含元素", x) }
輸出結果:
11 在s中的位置為: 3
如上,給定一個遞增順序的切片 s,呼叫Search(len(s), func(i int) bool { return S[i] >= x })
會返回data
中最小的索引i滿足s[i] >= 11
。如果呼叫者想要知道 11 是否在切片裡,它必須另外檢查s[i] == 11
。搜尋遞減順序的資料時,應使用<=
運算子代替>=
運算子。
自定義型別的排序
如上所述,只要自定義型別實現了sort.Interface
介面及其方法,該型別的排序就可以通過使用sort.Sort(slice)
實現。如下,學生成績排序的例子:
package main import ( "fmt" "sort" ) type StuScore struct { namestring score int } type StuScores []StuScore //Len() func (s StuScores)Len()int { return len(s) } //Less():成績將有高到低排序 func (s StuScores)Less(i, jint)bool { return s[i].score < s[j].score } //Swap() func (s StuScores)Swap(i, jint) { s[i], s[j] = s[j], s[i] } func main() { students := StuScores{ {"fzy", 95}, {"qwe", 91}, {"rty", 96}, {"asd", 90}} fmt.Println("未排序:") for _, v := range students { fmt.Println(v.name, ":", v.score) } fmt.Println() //StuScores已經實現了sort.Interface介面 sort.Sort(students) fmt.Println("按成績升序排序後:") for _, v := range students { fmt.Println(v.name, ":", v.score) } }
輸出結果:
未排序: fzy : 95 qwe : 91 rty : 96 asd : 90 按成績升序排序後: asd : 90 qwe : 91 fzy : 95 rty : 96
上面的示例實現的是升序排序,如果要得到降序排序結果,其實只要修改Less()
函式:
//Less():成績降序排序,只將小於號修改為大於號 func (s StuScores)Less(i, jint)bool { return s[i].score < s[j].score }
當然,也可以使用Reverse函式
來實現:
將 sort.Sort(students) 更改為: sort.Sort(sort.Reverse(StuScores(students)))// 按照成績的降序排序
參考資料:
ofollow,noindex">golang sort —— 排序演算法