[Go语言tips01]浅谈sort包

0. 引言

用C++时候就是用STL库里的sort函数,非常方便好用,看了一下Go语言的标准库,意料之内其中也是有相关的sort包。

重新学习了一下sort包,把这篇博客又优化了一遍,让它更能为我以后查阅提供方便!——23/04/28

1. 常用类型切片排序

默认是升序(从小到大),想要降序的话可以使用Reverse函数,之后也会提到。

1.1 Ints - 整数切片排序

func main() {
	s := []int{1, -1, 6, 4, 3, 8, 6, 234234, 8876, 234}
	sort.Ints(s)
	fmt.Println(s) // [-1 1 3 4 6 6 8 234 8876 234234]
}

可以通过func IntsAreSorted(a []int) bool来判断是否是升序,注意降序返回的也是false

1.2 Float64s - 浮点数切片排序

func main() {
	b := []float64{1.5, -1.6, 6.32326, 4.5545, 3.654654, 8.111, 6.2, 234234.9, 8876.0, 234.1}
	sort.Float64s(b)
	fmt.Println(b) // [-1.6 1.5 3.654654 4.5545 6.2 6.32326 8.111 234.1 8876 234234.9]
}

可以通过func Float64sAreSorted(a []float64) bool来判断是否是升序,注意降序返回的也是false

1.3 Strings - 字符串切片排序

func main() {
	b := []string{"a", "ab", "cc", "acd", "abbb"}
	sort.Strings(b)
	fmt.Println(b) // [a ab abbb acd cc]

}

可以通过func StringsAreSorted(a []string) bool来判断是否是升序,注意降序返回的也是false

2. 常用类型切片查找

可以用来替代二分查找法找某一个元素或者是某个元素的范围。

函数规则如下:

  1. 找到了指定元素,则返回首个指定元素的索引
  2. 未找到指定元素,则返回应当插入的位置索引

2.1 SearchInts - 整数切片查找

func main() {
	b := []int{1, 2, 3, 4, 4, 6, 7}
	l := sort.SearchInts(b, 4)
	r := sort.SearchInts(b, 5)
	fmt.Println(l, r) // 3 5
}

如果找某个元素可以直接使用SearchInts来查找获得索引l,再判断b[l]是否是指定元素即可。

如果找某个元素的范围可以先查找该元素获得左界限l(闭区间),然后再查找该元素+1来获得右界限r(开区间)。此时指定元素的范围是[l,r)、个数是r-l

必须保证切片是升序排序的才能返回正确的结果。

2.2 SearchFloat64s - 浮点数切片查找

func main() {
	b := []float64{1.1, 2.2, 3.4, 3.4, 4.6, 6.55, 7.89}
	x := sort.SearchFloat64s(b, 3.4)
	fmt.Println(x) // 2
}

必须保证切片是升序排序的才能返回正确的结果。

2.3 SearchStrings - 字符串切片查找

func main() {
	b := []string{"a", "ab", "abbb", "ac"}
	x := sort.SearchStrings(b, "abbb")
	fmt.Println(x) // 2
}

必须保证切片是升序排序的才能返回正确的结果。

3. sort包接口的函数

这是sort包里定义的一个接口,也就说必须要实现了下面的三个方法才行,之后的结构体排序也时实现了这个接口才能进行自定义排序。

type Interface interface {
    // Len方法返回集合中的元素个数
    Len() int
    // Less方法报告索引i的元素是否比索引j的元素小
    Less(i, j int) bool
    // Swap方法交换索引i和j的两个元素
    Swap(i, j int)
}

sort包也提前给[]int,[]float64,[]string准备了已经实现接口的type(T)

如果要使用后面提到的方法可以先把自己的切片做一个转换处理(或者直接声明指定类型)

a := []int{1, 2, 4, 3}          
b := []float64{1.3, 2.4, 4.5, 3.6}   
c := []string{"a", "aac", "ab", "dav"}
a = sort.IntSlice(a)            
b = sort.Float64Slice(b)            
c = sort.StringSlice(c)   

或者:

a := sort.IntSlice{1, 2, 4, 3}
b := sort.Float64Slice{1.3, 2.4, 4.5, 3.6}
c := sort.StringSlice{"a", "aac", "ab", "dav"}

下面以整数数组为例来介绍常用的参数是Interface类的函数。

3.1 Sort

调用1次data.Len确定长度,调用O(n*log(n))次data.Less和data.Swap。

本函数不能保证排序的稳定性(即不保证相等元素的相对次序不变)。

func main() {
	n := sort.IntSlice{9, 8, 1, 6, 3, 0, -1, 99, 32, 7}
    sort.Sort(n) // n.Sort() 实际上这个类有Sort方法调用sort.Sort
	fmt.Println(n) // [-1 0 1 3 6 7 8 9 32 99]
} 

这个方法其实对于简单的整数是很鸡肋的,毕竟可以直接sort.Ints(n)排序。

3.2 Stable

调用1次data.Len,O(n * log(n))次data.Less和O(n * log(n) * log(n))次data.Swap。

Stable排序data,并保证排序的稳定性,相等元素的相对次序不变。

func main() {
	n := sort.IntSlice{9, 8, 1, 6, 3, 0, -1, 99, 32, 7}
	sort.Stable(n)
	fmt.Println(n) // [-1 0 1 3 6 7 8 9 32 99]
} 

StableSort的区别就是Stable可以保证相同元素的相对位置不变,但是耗时会多一点。

3.3 Reverse

Reverse包装一个Interface接口并返回一个新的Interface接口对该接口排序可生成递减序列。

func main() {
	n := sort.IntSlice{9, 8, 1, 6, 3, 0, -1, 99, 32, 7}
	sort.Sort(sort.Reverse(n))
	fmt.Println(n) // [99 32 9 8 7 6 3 1 0 -1]
}

需要注意的是Reverse需要搭配Sort使用,实际上就是返回了一个改写了Less方法的Interface接口。

4. 结构体排序

这一点是重中之重,也是这篇博客的核心,因为单独为某一个数组实现Interface接口大多数情况是没有必要的……

4.1 实现Interface接口

核心就是我们要使用sort.Sort进行排序,它的参数是Interface接口因此我们的类需要实现这个接口,也就是同时有LenSwapLess三个方法,前两个方法基本上都一样关键是第三个Less方法决定了排序的方式。

type Person struct {
	Name string
	Age  int
	Star int
}
type Man []Person // Man是Person切片

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

上面的就是Less就是按照年龄的升序排列的。

4.2 单条件排序

如:年龄小的排在前面。

func (p Person) String() string {
	return fmt.Sprintf("%s: %d %d ,", p.Name, p.Age, p.Star)
}

type Man []Person

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

func main() {
	people := Man{
		{"Bob", 20, 99},
		{"John", 21, 80},
		{"Michael", 23, 70},
		{"Jenny", 21, 81},
	}
	fmt.Println(people)
	sort.Sort(people)
	fmt.Println(people)
} 
/*
[Bob: 20 99 , John: 21 80 , Michael: 23 70 , Jenny: 21 81 ,]
[Bob: 20 99 , John: 21 80 , Jenny: 21 81 , Michael: 23 70 ,]
*/

4.3 多条件排序

如:首先年龄小的在前面,年龄相同Star多的在前面。

func (p Person) String() string {
	return fmt.Sprintf("%s: %d %d ,", p.Name, p.Age, p.Star)
}

type Man []Person

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

func main() {
	people := Man{
		{"Bob", 20, 99},
		{"John", 21, 80},
		{"Michael", 23, 70},
		{"Jenny", 21, 81},
	}
	fmt.Println(people)
	sort.Sort(people)
	fmt.Println(people)
}
/*
[Bob: 20 99 , John: 21 80 , Michael: 23 70 , Jenny: 21 81 ,]
[Bob: 20 99 , Jenny: 21 81 , John: 21 80 , Michael: 23 70 ,]
*/

4.4 补充

通过改写String方法来实现的输出结构体的内容,Get!

func (p Person) String() string {
	return fmt.Sprintf("%s: %d %d ,", p.Name, p.Age, p.Star)
}
posted @   WtcSky  阅读(89)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· 单线程的Redis速度为什么快?
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
点击右上角即可分享
微信分享提示