beautifulremi / Golang - Sort 排序和搜索

Created Fri, 03 Oct 2025 14:03:07 +0800 Modified Mon, 23 Mar 2026 05:26:53 +0000
1300 Words

Go 的 sort 包设计得非常优雅和灵活,它不仅仅是一个函数,而是一整套工具,其核心是基于一个接口 (sort.Interface) 来实现的。

我们从最简单的用法开始,逐步深入到其核心设计。


1. 基础用法:为基本类型切片排序

对于 Go 内置的几种基本数据类型,sort 包提供了非常方便的函数,你无需做任何额外工作即可直接使用。

  • sort.Ints(slice): 对 []int 切片进行升序排序。

  • sort.Float64s(slice): 对 []float64 切片进行升序排序。

  • sort.Strings(slice): 对 []string 切片进行字典序升序排序。

这些函数都是原地排序,意味着它们会直接修改传入的切片,而不是返回一个新的排好序的切片。


2. 核心用法:sort.Sort() 和 sort.Interface (排序任意数据)

如果你想排序一个自定义结构体的切片,比如按照用户的年龄或姓名排序,就需要用到 sort 包最核心的功能。

Go 的 sort.Sort(data Interface) 函数可以排序任何满足 sort.Interface 接口的数据类型。这个接口定义了排序算法所需要知道的三个基本操作:

type Interface interface {
    // Len 是集合中的元素个数
    Len() int
    // Less 报告索引 i 的元素是否应该排在索引 j 的元素之前
    Less(i, j int) bool
    // Swap 交换索引 i 和 j 的元素
    Swap(i, j int)
}

步骤如下:

  1. 创建一个你的自定义类型的切片。

  2. 为这个切片类型定义 Len()Less(i, j int), 和 Swap(i, j int) 这三个方法。

  3. 调用 sort.Sort() 函数,将你的切片实例传入。


3. 便捷用法:sort.Slice() (更现代的方式)

实现完整的 sort.Interface 有时候会显得有些繁琐。从 Go 1.8 开始,sort 包提供了一个更便捷的函数 sort.Slice。你不需要定义新类型或实现三个方法,只需要提供一个比较函数(通常是一个闭包)。

func Slice(slice any, less func(i, j int) bool)

  • slice: 你想要排序的切片。

  • less: 一个函数,它定义了 slice[i] 是否应该排在 slice[j] 前面。

package main

import (
	"fmt"
	"sort"
)

type Person struct {
	Name string
	Age  int
}

func main() {
	people := []Person{
		{"Alice", 30},
		{"Bob", 25},
		{"Charlie", 35},
		{"David", 20},
	}
	fmt.Println("原始数据:", people)

	// --- 场景1: 按年龄升序排序 ---
	sort.Slice(people, func(i, j int) bool {
		return people[i].Age < people[j].Age
	})
	fmt.Println("按年龄升序:", people)

	// --- 场景2: 按姓名(字典序)排序 ---
	sort.Slice(people, func(i, j int) bool {
		return people[i].Name < people[j].Name
	})
	fmt.Println("按姓名升序:", people)
}

sort.Slice 极其灵活,因为你可以随时定义不同的 less 函数来对同一个数据结构进行多种维度的排序,而无需创建多个类型。


4. 其他有用的 sort 函数

  • sort.IsSorted(data Interface): 检查一个集合是否已经排好序。对于基础类型,也有 sort.IntsAreSorted() 等函数。

  • sort.Search(n int, f func(int) bool): 在一个已排序的集合中进行二分查找。它会找到满足 f(i) 为 true 的最小索引 i。常用于在有序数据中高效地查找某个值。

sort.Search 要求你所搜索的数据范围必须是“有序的”。这里的“有序”指的是,对于你要判断的条件 f,这个范围内的索引可以被分为两部分:

  1. 前面一部分索引,f(i) 的结果全部是 false

  2. 后面一部分索引,f(i) 的结果全部是 true

sort.Search 的任务是找到那个从 false 变成 true 的临界点,也就是第一个让 f(i) 返回 true 的索引 i

func Search(n int, f func(int) bool) int
  • n int: 定义了搜索范围是 [0, n)。通常,如果你在切片 s 中搜索,n 就是 len(s)

  • f func(int) bool: 这是一个“断言函数”或“判定函数”。你提供一个函数 f,它接受一个索引 i 作为参数,并返回 true 或 false。这个函数定义了你的搜索条件

如果元素不存在,sort.Search 会返回这个元素应该被插入的位置,以保持切片的有序性。

对于基本类型,sort 包提供了一些方便的包装函数,你无需自己编写闭包。

  • sort.SearchInts(s []int, x int)

  • sort.SearchFloat64s(s []float64, x float64)

  • sort.SearchStrings(s []string, x string)

sort.SearchInts(s, x) 的内部实现就等同于 sort.Search(len(s), func(i int) bool { return s[i] >= x })