該包實現了四種基本排序算法:插入排序、歸並排序、堆排序和快速排序。 但是這四種排序方法是不公開的,它們只被用於sort包內部使用。所以在對數據集合排序時不必考慮應當選擇哪一種排序方法,只要實現了sort.Interface定義的三個方法:獲取數據集合長度的Len()方法、比較兩個元素大小的Less()方法和交換兩個元素位置的Swap()方法,就可以順利對數據集合進行排序。sort包會根據實際數據自動選擇高效的排序算法。 除此之外,為了方便對常用數據類型的操作,sort包提供了對[]int切片、[]float64切片和[]string切片完整支持,主要包括:
對基本數據類型切片的排序支持
基本數據元素查找
判斷基本數據類型切片是否已經排好序
對排好序的數據集合逆序
3.1.1 數據集合排序
前面已經提到過,對數據集合(包括自定義數據類型的集合)排序需要實現sort.Interface接口的三個方法,我們看以下該接口的定義:
type Interface interface {
// 獲取數據集合元素個數
Len() int
// 如果i索引的數據小於j所以的數據,返回true,不會調用
// 下面的Swap(),即數據升序排序。
Less(i, j int) bool
// 交換i和j索引的兩個元素的位置
Swap(i, j int)
}
數據集合實現了這三個方法后,即可調用該包的Sort()方法進行排序。 Sort()方法定義如下:
`func Sort(data Interface)`
Sort()方法惟一的參數就是待排序的數據集合。
該包還提供了一個方法可以判斷數據集合是否已經排好順序,該方法的內部實現依賴於我們自己實現的Len()和Less()方法:
func IsSorted(data Interface) bool {
n := data.Len()
for i := n - 1; i > 0; i-- {
if data.Less(i, i-1) {
return false
}
}
return true
}
下面是一個使用sort包對學生成績排序的示例:
package main
import (
"fmt"
"sort"
)
//學生成績結構體
type StuScore struct {
//姓名
name string
//成績
score int
}
type StuScores []StuScore
//Len()
func (s StuScores) Len() int {
return len(s)
}
//Less():成績將有低到高排序
func (s StuScores) Less(i, j int) bool {
return s[i].score < s[j].score
}
//Swap()
func (s StuScores) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func main() {
stus := StuScores{
{"alan", 95},
{"hikerell", 91},
{"acmfly", 96},
{"leao", 90}}
fmt.Println("Default:")
//原始順序
for _, v := range stus {
fmt.Println(v.name, ":", v.score)
}
fmt.Println()
//StuScores已經實現了sort.Interface接口
sort.Sort(stus)
fmt.Println("Sorted:")
//排好序后的結構
for _, v := range stus {
fmt.Println(v.name, ":", v.score)
}
//判斷是否已經排好順序,將會打印true
fmt.Println("IS Sorted?", sort.IsSorted(stus))
}
程序該示例程序的自定義類型StuScores實現了sort.Interface接口,所以可以將其對象作為sort.Sort()和sort.IsSorted()的參數傳入。運行結果:
======Default======
alan : 95
hikerell : 91
acmfly : 96
leao : 90
======Sorted=======
leao : 90
hikerell : 91
alan : 95
acmfly : 96
IS Sorted? true
該示例實現的是升序排序,如果要得到降序排序結果,其實只要修改Less()函數:
//Less():成績降序排序,只將小於號修改為大於號
func (s StuScores) Less(i, j int) bool {
return s[i].score > s[j].score
}
此外,sort包提供了Reverse()方法,可以允許將數據按Less()定義的排序方式逆序排序,而不必修改Less()代碼。方法定義如下:
func Reverse(data Interface) Interface
我們可以看到Reverse()返回的一個sort.Interface接口類型,整個Reverse()的內部實現比較有趣:
//定義了一個reverse結構類型,嵌入Interface接口
type reverse struct {
Interface
}
//reverse結構類型的Less()方法擁有嵌入的Less()方法相反的行為
//Len()和Swap()方法則會保持嵌入類型的方法行為
func (r reverse) Less(i, j int) bool {
return r.Interface.Less(j, i)
}
//返回新的實現Interface接口的數據類型
func Reverse(data Interface) Interface {
return &reverse{data}
}
了解內部原理后,可以在學生成績排序示例中使用Reverse()來實現成績升序排序:
sort.Sort(sort.Reverse(stus))
for _, v := range stus {
fmt.Println(v.name, ":", v.score)
}
最后一個方法:Search()
func Search(n int, f func(int) bool) int
官方文檔這樣描述該方法:
Search()方法回使用“二分查找”算法來搜索某指定切片[0:n],並返回能夠使f(i)=true的最 小的i(0<=i<n)值,並且會假定,如果f(i)=true,則f(i+1)=true,即對於切片[0:n],
i之前的切片元素會使f()函數返回false,i及i之后的元素會使f()函數返回true。但是,當 在切片中無法找到時f(i)=true的i時(此時切片元素都不能使f()函數返回true),Search() 方法會返回n。
Search()函數一個常用的使用方式是搜索元素x是否在已經升序排好的切片s中:
x := 11
s := []int{3, 6, 8, 11, 45} //注意已經升序排序
pos := sort.Search(len(s), func(i int) bool { return s[i] >= x })
if pos < len(s) && s[pos] == x {
fmt.Println(x, "在s中的位置為:", pos)
} else {
fmt.Println("s不包含元素", x)
}
官方文檔還給出了一個猜數字的小程序:
func GuessingGame() {
var s string
fmt.Printf("Pick an integer from 0 to 100.\n")
answer := sort.Search(100, func(i int) bool {
fmt.Printf("Is your number <= %d? ", i)
fmt.Scanf("%s", &s)
return s != "" && s[0] == 'y'
})
fmt.Printf("Your number is %d.\n", answer)
}
3.1.2 sort包已經支持的內部數據類型排序
前面已經提到,sort包原生支持[]int、[]float64和[]string三種內建數據類型切片的排序操作,即不必我們自己實現相關的Len()、Less()和Swap()方法。
- IntSlice類型及[]int排序
由於[]int切片排序內部實現及使用方法與[]float64和[]string類似,所以只詳細描述該部分。
sort包定義了一個IntSlice類型,並且實現了sort.Interface接口:
type IntSlice []int
func (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] }
//IntSlice類型定義了Sort()方法,包裝了sort.Sort()函數
func (p IntSlice) Sort() { Sort(p) }
//IntSlice類型定義了SearchInts()方法,包裝了SearchInts()函數
func (p IntSlice) Search(x int) int { return SearchInts(p, x) }
並且提供的sort.Ints()方法使用了該IntSlice類型:
`func Ints(a []int) { Sort(IntSlice(a)) }`
所以,對[]int切片排序是更常使用sort.Ints(),而不是直接使用IntSlice類型:
s := []int{5, 2, 6, 3, 1, 4} // 未排序的切片數據
sort.Ints(s)
fmt.Println(s) //將會輸出[1 2 3 4 5 6]
如果要使用降序排序,顯然要用前面提到的Reverse()方法:
s := []int{5, 2, 6, 3, 1, 4} // 未排序的切片數據
sort.Sort(sort.Reverse(sort.IntSlice(s)))
fmt.Println(s) //將會輸出[6 5 4 3 2 1]
如果要查找整數x在切片a中的位置,相對於前面提到的Search()方法,sort包提供了SearchInts():
`func SearchInts(a []int, x int) int`
注意,SearchInts()的使用條件為:切片a已經升序排序
s := []int{5, 2, 6, 3, 1, 4} // 未排序的切片數據
sort.Ints(s) //排序后的s為[1 2 3 4 5 6]
fmt.Println(sort.SearchInts(s, 3)) //將會輸出2
- Float64Slice類型及[]float64排序
實現與Ints類似,只看一下其內部實現:
type Float64Slice []float64
func (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] }
func (p Float64Slice) Sort() { Sort(p) }
func (p Float64Slice) Search(x float64) int { return SearchFloat64s(p, x) }
與Sort()、IsSorted()、Search()相對應的三個方法:
func Float64s(a []float64)
func Float64sAreSorted(a []float64) bool
func SearchFloat64s(a []float64, x float64) int
要說明一下的是,在上面Float64Slice類型定義的Less方法中,有一個內部函數isNaN()。 isNaN()與math包中IsNaN()實現完全相同,sort包之所以不使用math.IsNaN(),完全是基於包依賴性的考慮,應當看到,sort包的實現不依賴與其他任何包。
- StringSlice類型及[]string排序
兩個string對象之間的大小比較是基於“字典序”的。
實現與Ints類似,只看一下其內部實現:
type StringSlice []string
func (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] }
func (p StringSlice) Sort() { Sort(p) }
func (p StringSlice) Search(x string) int { return SearchStrings(p, x) }
與Sort()、IsSorted()、Search()相對應的三個方法:
func Strings(a []string)
func StringsAreSorted(a []string) bool
func SearchStrings(a []string, x string) int
