Skip to main content

Std Sort

Go 语言标准库中的 sort 包提供了多种排序功能,支持对切片和自定义集合进行排序。sort 包中的函数和接口使得排序操作非常灵活和高效。

基本用法

对整数切片排序

package main

import (
"fmt"
"sort"
)

func main() {
ints := []int{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5}
sort.Ints(ints)
fmt.Println("Sorted ints:", ints)
}

对字符串切片排序

package main

import (
"fmt"
"sort"
)

func main() {
strs := []string{"banana", "apple", "cherry"}
sort.Strings(strs)
fmt.Println("Sorted strings:", strs)
}

对浮点数切片排序

package main

import (
"fmt"
"sort"
)

func main() {
floats := []float64{3.14, 1.41, 2.71, 1.62}
sort.Float64s(floats)
fmt.Println("Sorted floats:", floats)
}

自定义排序

对于复杂的数据结构或需要自定义排序逻辑的场景,可以实现 sort.Interface 接口。该接口包括三个方法:

  • Len() int:返回集合的长度。
  • Less(i, j int) bool:如果索引 i 处的元素小于索引 j 处的元素,则返回 true
  • Swap(i, j int):交换索引 i 和索引 j 处的元素。

示例:对结构体切片排序

假设我们有一个包含人员信息的结构体,并希望按年龄对其进行排序:

package main

import (
"fmt"
"sort"
)

// 定义结构体
type Person struct {
Name string
Age int
}

// 实现 sort.Interface 接口
type ByAge []Person

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

func main() {
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35},
}

// 使用 sort.Sort 进行排序
sort.Sort(ByAge(people))
fmt.Println("Sorted by age:", people)
}

逆序排序

sort 包提供了 sort.Reverse 函数,可以将排序顺序反转:

package main

import (
"fmt"
"sort"
)

func main() {
ints := []int{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5}
sort.Sort(sort.Reverse(sort.IntSlice(ints)))
fmt.Println("Sorted ints in reverse:", ints)
}

检查是否已排序

sort 包还提供了检查切片是否已排序的函数:

  • sort.IntsAreSorted([]int) bool
  • sort.StringsAreSorted([]string) bool
  • sort.Float64sAreSorted([]float64) bool

示例

package main

import (
"fmt"
"sort"
)

func main() {
ints := []int{1, 2, 3, 4, 5}
fmt.Println("Ints are sorted:", sort.IntsAreSorted(ints))

strs := []string{"banana", "apple", "cherry"}
fmt.Println("Strings are sorted:", sort.StringsAreSorted(strs))
}

总结

Go 语言的 sort 包提供了丰富的排序功能,支持对基本类型切片进行排序,并允许通过实现 sort.Interface 接口对自定义数据类型进行排序。此外,还提供了反转排序顺序和检查是否已排序的函数。通过这些功能,开发者可以方便地对各种数据进行排序操作。

Search 二分法查找

Go 语言标准库中的 sort 包不仅提供了排序功能,还提供了二分查找(binary search)功能,可以高效地在已排序的切片中查找元素。sort 包的搜索 API 包括以下函数:

  • sort.Search(int, func(int) bool) int
  • sort.SearchInts([]int, int) int
  • sort.SearchStrings([]string, string) int
  • sort.SearchFloat64s([]float64, float64) int

基本用法

sort.Search

sort.Search 是一个通用的二分查找函数,它接受一个整数 n 和一个返回布尔值的函数 f。它在 [0, n) 范围内查找最小的索引 i,使得 f(i)true

package main

import (
"fmt"
"sort"
)

func main() {
// 查找第一个大于等于 3 的索引
n := 10
index := sort.Search(n, func(i int) bool { return i >= 3 })
fmt.Println("Index:", index) // 输出:Index: 3
}

sort.SearchInts

sort.SearchInts 在已排序的整数切片中查找指定的整数,返回该整数的索引。如果找不到,则返回该整数应插入的位置,以保持切片有序。

package main

import (
"fmt"
"sort"
)

func main() {
ints := []int{1, 2, 3, 4, 5, 6}
index := sort.SearchInts(ints, 4)
fmt.Println("Index of 4:", index) // 输出:Index of 4: 3

index = sort.SearchInts(ints, 7)
fmt.Println("Index of 7:", index) // 输出:Index of 7: 6(应插入的位置)
}

sort.SearchStrings

sort.SearchStrings 在已排序的字符串切片中查找指定的字符串,返回该字符串的索引。如果找不到,则返回该字符串应插入的位置,以保持切片有序。

package main

import (
"fmt"
"sort"
)

func main() {
strs := []string{"apple", "banana", "cherry"}
index := sort.SearchStrings(strs, "banana")
fmt.Println("Index of banana:", index) // 输出:Index of banana: 1

index = sort.SearchStrings(strs, "date")
fmt.Println("Index of date:", index) // 输出:Index of date: 3(应插入的位置)
}

sort.SearchFloat64s

sort.SearchFloat64s 在已排序的浮点数切片中查找指定的浮点数,返回该浮点数的索引。如果找不到,则返回该浮点数应插入的位置,以保持切片有序。

package main

import (
"fmt"
"sort"
)

func main() {
floats := []float64{1.1, 2.2, 3.3, 4.4}
index := sort.SearchFloat64s(floats, 3.3)
fmt.Println("Index of 3.3:", index) // 输出:Index of 3.3: 2

index = sort.SearchFloat64s(floats, 5.5)
fmt.Println("Index of 5.5:", index) // 输出:Index of 5.5: 4(应插入的位置)
}

总结

Go 语言标准库中的 sort 包提供了强大的二分查找功能,可以在已排序的切片中高效地查找元素。通过这些搜索 API,开发者可以轻松地在整数、字符串和浮点数切片中进行查找操作,或者通过实现自定义的查找逻辑来满足特定需求。

sort.Interface 三个函数的作用

在 Go 语言中,sort.Interface 接口定义了三个方法,任何实现了这三个方法的类型都可以使用 sort.Sort 函数进行排序。具体来说,这三个方法是:

  1. Len() int
  2. Less(i, j int) bool
  3. Swap(i, j int)

下面我们详细介绍这三个方法的作用和用法。

Len() int

Len 方法返回集合中的元素数量。排序算法需要知道集合的长度以便确定排序的范围。

func (a ByAge) Len() int {
return len(a)
}

Less(i, j int) bool

Less 方法定义了两个元素之间的排序顺序。如果元素 i 应该排在元素 j 之前,返回 true;否则返回 false。这个方法是排序的核心,用于比较两个元素的大小。

func (a ByAge) Less(i, j int) bool {
return a[i].Age < a[j].Age
}

Swap(i, j int)

Swap 方法交换集合中两个元素的位置。排序算法在排序过程中会频繁地调用这个方法来交换元素。

func (a ByAge) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}

示例:对自定义数据类型排序

下面是一个完整的示例,展示了如何使用 sort.Interface 接口对自定义数据类型进行排序。

package main

import (
"fmt"
"sort"
)

// 定义结构体
type Person struct {
Name string
Age int
}

// 实现 sort.Interface 接口
type ByAge []Person

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

func main() {
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35},
}

fmt.Println("Before sorting:", people)
sort.Sort(ByAge(people))
fmt.Println("After sorting:", people)
}

输出:

Before sorting: [{Alice 30} {Bob 25} {Charlie 35}]
After sorting: [{Bob 25} {Alice 30} {Charlie 35}]

在这个示例中,我们定义了一个 Person 结构体,并创建了一个 ByAge 类型,该类型是 Person 结构体的切片。然后我们为 ByAge 类型实现了 LenLessSwap 方法,使其满足 sort.Interface 接口。最后,我们使用 sort.Sort 函数对 people 切片按年龄进行排序。

总结

sort.Interface 接口的三个方法

  • Len :获取集合的长度
  • Less : 比较两个元素的大小
  • Swap: 交换两个元素的位置。

方法重写

你提到的super.Less(j, i)的概念类似于其他面向对象编程语言(如 Java、C++)中的“super”关键字,用于调用父类的方法。在 Go 语言中,没有super关键字,但我们可以通过嵌入接口或结构体的方式实现类似的功能。

在 Go 语言中,通过嵌入式接口或结构体,我们可以显式地调用嵌入的接口或结构体的方法,从而实现类似于“super”的行为。具体到你的例子中,我们通过r.Interface.Less(j, i)来调用嵌入的Interface接口的方法。

具体示例

假设我们有一个接口Interface,定义如下:

type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}

然后我们有一个实现了这个接口的类型IntSlice

type IntSlice []int

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] }

我们定义了一个reverse结构体,它嵌入了Interface接口,并重写了Less方法:

type reverse struct {
Interface
}

func (r reverse) Less(i, j int) bool {
return r.Interface.Less(j, i) // 这里是sort.go的源码,虽然go有方法集合提升,但是不能直接调用r.Less(j,i),自身调用自身,会死循环
}

在这个例子中,r.Interface.Less(j, i)的作用类似于其他语言中的super.Less(j, i),它显式地调用了嵌入的Interface接口的Less方法。

使用示例

我们可以通过Reverse函数来创建一个reverse结构体的实例,并使用它来反转排序顺序:

func Reverse(data Interface) Interface {
return &reverse{data}
}

func main() {
data := IntSlice{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5}
sort.Sort(Reverse(data))
fmt.Println(data) // 输出: [9 6 5 5 5 4 3 3 2 1 1]
}

在这个例子中,sort.Sort(Reverse(data))会对数据进行反转排序。Reverse(data)返回一个reverse结构体实例,这个实例的Less方法会调用嵌入的Interface接口的Less方法,但参数顺序是反转的,从而实现反转排序。

总结

通过嵌入接口或结构体,并显式调用嵌入的方法,Go 语言实现了类似于其他语言中super关键字的功能。这使得我们可以在重写方法时调用“父类”的方法,从而实现更复杂的行为。