对数据集合(包括自定义数据类型的集合)排序需要实现 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},
}
// 打印未排序的 stus 数据
fmt.Println("Default:\n\t",stus)
//StuScores 已经实现了 sort.Interface 接口 , 所以可以调用 Sort 函数进行排序
sort.Sort(stus)
// 判断是否已经排好顺序,将会打印 true
fmt.Println("IS Sorted?\n\t", sort.IsSorted(stus))
// 打印排序后的 stus 数据
fmt.Println("Sorted:\n\t",stus)
}
该示例程序的自定义类型 StuScores 实现了 sort.Interface 接口,所以可以将其对象作为 sort.Sort() 和 sort.IsSorted() 的参数传入。运行结果:
Default:
[{alan 95} {hikerell 91} {acmfly 96} {leao 90}]
IS Sorted?
true
Sorted:
[{leao 90} {hikerell 91} {alan 95} {acmfly 96}]
该示例实现的是升序排序,如果要得到降序排序结果,其实只要修改 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))
fmt.Println(stus)
最后一个方法:Search()
func Search(n int, f func(int) bool) int
该方法会使用“二分查找”算法来找出能使 f(x)(0<=x<n) 返回 ture 的最小值 i。
前提条件 : f(x)(0<=x<i) 均返回 false, f(x)(i<=x<n) 均返回 ture。
如果不存在 i 可以使 f(i) 返回 ture, 则返回 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)
}