命令记不住

登陆

标准库 sort 包

1.sort包介绍

使用sort包进行排序时无需具体考虑使用哪种排序方式

func insertionSort(data Interface,a,b int)
func heapSort(data Interface,a,b int)
func quickSort(data Interface,a,b,maxDepth int)

sort.Interface接口定义了三个方法。注意sort包中接口Interface这个名字,是以大些字母I开头,不要和interface关键字混淆,这里就是一个接口名而已

type Interface interface{
//Len 为集合内元素的总数
Len() int
//如果index为i的元素小于index为j的元素,则返回true,否则返回false
Less(i,j int)bool
//Swap 交换索引为i和j的元素
Swap(i,j int)
}

这三个方法分别是:获取数据集合长度的Len()方法、比较两个元素大小的Less()方法和交换两个元素位置的Swap()方法。只要实现了这三个方法,就可以对数据集合进行排序,sort包会根据实际数据自动选择高效的排序算法

sort包原生支持[]int、[]float64和[]string三种内建数据类型切片的排序操作,既不必程序员自己实现相关的Len()、Less()、和Swap()方法

1.sort包如何定义排序操作
type IntSlice []int

先通过[]int来定义新类型IntSlice,然后在IntSlice上定义三个方法Len(),Less(i,j int),Swap(i,j int),实现了这三个方法也就意味着实现了sort.Interface接口

方法func(p InSlice) Sort() 通过调用sort.Sort(P)函数来实现排序,而Sort(p)需要参数p是sort.Interface类型,但IntSlice实现了这三个接口方法,也就是sort.Interface类型,因此可以调用其排序结果,其他[]float64和[]string的排序也基本上按照这种方式来实现

其他类型并没有在包中给出实现方法,需要自己定义实现

func (p IntSlice)Len()int
func (p IntSlice)Less(i,j int)bool{return p[i]<p[j]}
func (p IntSlice)Swap(i,j int)
func (p IntSlice)Sort(){Sort(p)}

如下是[]int和[]string排序的实例

package main

import (
    "fmt"
    "sort"
)

func main() {
    a := []int{33, 5, 3, -6, 19, 11, -14}
    sort.Ints(a)
    fmt.Println("排序:", a)

    sort.Sort(sort.Reverse(sort.IntSlice(a)))
    fmt.Println("降序: ", a)

    s := []string{"surface", "ipad", "Lenovo", "mac", "thinkpad", "联想"}
    sort.Strings(s)
    fmt.Println("排序:", s)

    sort.Sort(sort.Reverse(sort.StringSlice(s)))
    fmt.Printf("降序: %v\n", s)
}

程序输出:

排序: [-14 -6 3 5 11 19 33]
降序:  [33 19 11 5 3 -6 -14]
排序: [Lenovo ipad mac surface thinkpad 联想]
降序: [联想 thinkpad surface mac ipad Lenovo]

默认结果都是升序排列,如果使用sort.Reverse则可进行降序排列

2.sort包相关方法功能的注释
package main

//将类型为floadt64的slice以升序方式排序
func Float64s(a []float64)

//判断是否已经进行排序
func Float64sAreSorted(a []float64) bool

//Ints以升序排列int切片
func Ints(a []int)

//判断int切片是否已经按升序排列
func IntsAreSorted(a []int) bool

//IsSorted判断数据是否已经排列。包括各种可排序的数据类型判断
func IsSorted(data Interface) bool

//String以升序排列string切片
func Strings(a []string)

//判断string切片是否按升序排列
func StringsAreSorted(a []string) bool

//search使用二分法进行查找,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而不是返回-1

//Search常用于在一个已排序的、可索引的数据结构中寻找索引为i的值x,例如数组或切片
//这种情况下实参f一般是一个闭包,会捕获所要搜索的值,以及索引并排序该数据结构的方式
func Search(n int,f func(int)bool)int

//SearchFloat64s 在float64切片中搜索x并返回索引,如Search函数那样
//返回可以插入x值的索引位置,如果x不存在,返回数组a的长度切片必须以升序排列
func SearchFloat64s(a []float64,x float64)int

//SearchInts在ints切片搜索x并返回索引,如Search函数那样,返回可以插入x值的索引位置,如果x不存在,返回数组a的长度切片必须以升序排列
func SearchInts(a []int,x int)int

//SearchFloat64s 在string切片中搜索x并返回索引,如Search函数所述,返回可以插入x值的索引位置,如果x不存在,返回数组a的长度切片必须以升序排序
func SearchStrings(a []string,x string)int

//其中需要注意的是,以上三种查找方法对应的slice必须按照升序进行排序,否则会出现奇怪的结果

//Sort 对 data进行排序,它调用一次data.Len 来决定排序的长度n 调用data.Less和data.Swap的开销为O(n*log(n)),此排序为不稳定排序,它根据不同形式决定使用
//不同的排序方法(插入排序、堆排序、快速排序)
func Sort(data Interface)

//Stable对data进行排序,排序过程中,如果data中存在相等的元素,则它们原来的顺序不会改变,既如果有两个相等的元素num 他们初始index分别为i和j,并且
//i < j ,则利用Stable 对data进行排序后,i依然小于j,直接利用sort进行排序则不能够保证这一点
func Stable(data Interface)

2.自定义sort.Interface排序

如果是[]int、[]float64和string之外的某个具体结构的排序,就需要自己实现sort.Interface了,数据集合(包括自定义数据类型的集合)排序需要实现sort.Interface接口的三个方法,既:Len()、Swap(i,j int)、Less(i,j int)数据集合实现这三个方法后,既可调用该包的Sort()方法进行排序。Sort(data Interface)方法内部会使用quickSort()来进行集合的排序。而quickSort()会根据实际情况来选择排序算法

任何实现了sort.Interface的类型(一般为集合),均可使用该包中的方法进行排序。这些方法要求集合内列出元素的索引为整数。

例如

package main

import (
    "fmt"
    "sort"
)

type person struct {
    Name string
    Age  int
}

type personSlice []person

func (s personSlice) Len() int           { return len(s) }
func (s personSlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s personSlice) Less(i, j int) bool { return s[i].Age < s[j].Age }

func main() {
    a := personSlice{
        {Name: "Jim", Age: 15},
        {Name: "石惊天", Age: 22},
        {Name: "Горький", Age: 57},
        {Name: "박 빅 브라더", Age: 32},
        {Name: "Li Wei", Age: 42},
    }
    sort.Sort(a)
    fmt.Println("Sort:", a)

    sort.Stable(a)
    fmt.Println("Stable:", a)

}

该示例程序的自定义类型personSlice实现了sort.Interface接口,所以可以将其对象作为sort.Sort()和sort.Stable()的参数传入,运行结果如下

Sort: [{Jim 15} {石惊天 22} {박 빅 브라더 32} {Li Wei 42} {Горький 57}]
Stable: [{Jim 15} {石惊天 22} {박 빅 브라더 32} {Li Wei 42} {Горький 57}]

3.sort.Slice排序

为每种类型提供一个特定的sort.Interface的实现很繁琐,可以利用sort.Slice函数,把Less(i,j int)作为一个比较回调函数,简单地传递给sort.Slice进行排序。这种方法虽然方便,但是不建议使用,因为在sort.Slice中使用了reflect

例如:

package main

import (
    "fmt"
    "sort"
)

type River struct {
    Name   string
    Length int
}

func main() {
    Rivers := []River{
        {"刚果河", 4640},
        {"尼罗河", 6670},
        {"亚马孙河", 6400},
        {"黄河", 5464},
        {"鄂毕河", 3650},
        {"恒河", 2510},
        {"密西西比河", 6021},
        {"叶尼塞河", 5539},
        {"长江", 6300},
    }

    // less作为回调函数
    sort.Slice(Rivers, func(i, j int) bool {
        return Rivers[i].Length >= Rivers[j].Length
    })
    fmt.Println(Rivers)

}

程序输入:

[{尼罗河 6670} {亚马孙河 6400} {长江 6300} {密西西比河 6021} {叶尼塞河 5539} {黄河 5464} {刚果河 4640} {鄂毕河 3650} {恒河 2510}]
推荐阅读