GO-深入理解Sort

Go 语言的 sort 包提供了对切片和用户自定义集合进行排序的功能。它的设计非常灵活,支持多种数据类型的排序,并且允许用户通过实现接口来自定义排序规则。以下是对 sort 包的深入解析,包括其核心接口、实现原理以及使用方法

核心接口

type Interface interface {
// Len 返回集合中的元素数量
Len() int

// Less 返回索引为 i 的元素是否应该排在索引为 j 的元素之前
Less(i, j int) bool

// Swap 交换索引为 i 和 j 的元素
Swap(i, j int)
}

排序函数

sort.Sort

  • 对实现了 sort.Interface 接口的类型进行排序

exmaple:

package main

import (
"fmt"
"sort"
)

type Person struct {
Name string
Age int
}

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", 25},
{"Bob", 30},
{"Charlie", 20},
}

sort.Sort(ByAge(people))
fmt.Println(people) // 输出: [{Charlie 20} {Alice 25} {Bob 30}]
}

sort.Slice

func Slice(slice interface{}, less func(i, j int) bool)
  • 对任意切片进行排序,通过传入一个比较函数来定义排序规则
package main

import (
"fmt"
"sort"
)

func main() {
nums := []int{3, 1, 4, 1, 5, 9, 2, 6}
sort.Slice(nums, func(i, j int) bool {
return nums[i] < nums[j]
})
fmt.Println(nums) // 输出: [1 1 2 3 4 5 6 9]
}

sort.SliceStable

func SliceStable(slice interface{}, less func(i, j int) bool)
  • 对任意切片进行稳定排序,通过传入一个比较函数来定义排序规则
package main

import (
"fmt"
"sort"
)

type Person struct {
Name string
Age int
}

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

sort.SliceStable(people, func(i, j int) bool {
return people[i].Age < people[j].Age
})

fmt.Println(people)
// 输出: [{David 20} {Alice 25} {Bob 25} {Charlie 30}]
}

sort.IsSorted

func IsSorted(data Interface) bool
  • 检查切片是否已经按照指定的规则排序
package main

import (
"fmt"
"sort"
)

func main() {
nums := []int{1, 2, 3, 4, 5}
fmt.Println(sort.IsSorted(sort.IntSlice(nums))) // 输出: true
}

排序原理

排序算法

  • **sort.Sort**:使用快速排序(QuickSort)的变体(内省排序)
  • **sort.Slice**:同样使用快速排序的变体(内省排序)
  • **sort.SliceStable**:使用归并排序(MergeSort)

内省排序(Introsort)

内省排序是快速排序、堆排序和插入排序的结合:

  1. 在大多数情况下,使用快速排序
  2. 当递归深度超过一定阈值时,切换到堆排序(避免快速排序的最坏情况)
  3. 对于小规模数据(如长度小于 12 的切片),使用插入排序

归并排序(MergeSort)

归并排序的核心是分治法:

  1. 将切片分成两部分
  2. 递归地对每一部分进行排序
  3. 将排序后的两部分合并
  4. 在合并过程中,如果两个元素相等,会优先保留左边部分的元素,从而保证排序的稳定性(所以不会改变原来的顺序)

自定义排序

  • 通过实现 sort.Interface 接口,可以对任意类型进行排序
package main

import (
"fmt"
"sort"
)

type ByLength []string

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

func main() {
fruits := []string{"peach", "banana", "kiwi"}
sort.Sort(ByLength(fruits))
fmt.Println(fruits) // 输出: [kiwi peach banana]
}

预定义类型

sort 包为一些常见类型提供了预定义的排序实现:

  • sort.IntSlice:对 []int 排序。
  • sort.Float64Slice:对 []float64 排序。
  • sort.StringSlice:对 []string 排序

排序性能对比

特性 sort.Slice(快速排序) sort.SliceStable(归并排序)
时间复杂度 平均 O(n log n),最坏 O(n^2) 始终 O(n log n)
空间复杂度 O(log n)(递归栈) O(n)(额外空间)
稳定性 不稳定 稳定
适用场景 不需要稳定性的场景 需要稳定性的场景