Go 学习笔记9 - 排序 |Go主题月

1,057 阅读3分钟

排序操作是我们经常会用到的操作,虽然网上一大把各种排序算法的实现,但是在 go 内置 sort 包中已经帮我们封装好了相关操作。我们不需要为多种类型定义不同的排序实现,这是非常方便的。它使用 sort.Interface 接口类型来进行排序,我们只需要为我们的数据类型实现它的方法就能够使用内置排序算法。

type Interface interface {
    // Len is the number of elements in the collection.
    Len() int
    // Less reports whether the element with
    // index i should sort before the element with index j.
    Less(i, j int) bool
    // Swap swaps the elements with indexes i and j.
    Swap(i, j int)
}

1.int、folat64、string 排序

sort 包为我们封装了一些开箱即用的函数,我们可以直接调用而不需要定义排序函数。目前 int、float64、string 的排序可以直接使用,其余类型需要们自己写一些操作函数。

a := []int{15, 3, 12, 7, 99, 5, 3, 7, 4}
sort.Ints(a)
fmt.Println(a) // [3 3 4 5 7 7 12 15 99]

2.自定义类型排序

定义 type Data []int64 实现 Len、Less、Swap 方法,然后使用 sort.Sort 排序。

Len:获取数组元素个数、Less 比较两个元素、Swap 交换元素。

type Data []int64

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

func main() {
    a := []int64{15, 3, 12, 7, 99, 5, 3, 7, 4}
    sort.Sort(Data(a))
    fmt.Println(a) // [3 3 4 5 7 7 12 15 99]
}

3.结构体排序

我们可能会对结构体里面的某个字段进行排序,这时也是需要实现 sort.Interface 接口。


type Student struct {
    Name   string
    Age    int
    Height int // cm
    Weight int // kg
}

var students = []*Student{
    {"张三", 15, 160, 40},
    {"李四", 18, 168, 50},
    {"苏珊", 18, 165, 43},
    {"丽丽", 16, 162, 33},
    {"王五", 17, 170, 50},
}

type Sts []*Student

func (s Sts) Len() int {
    return len(s)
}

func (s Sts) Less(i, j int) bool {
    return s[i].Weight < s[j].Weight
}

func (s Sts) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}

func main() {
    sort.Sort(Sts(students))
    for _, s := range students {
        fmt.Printf("%+v\n", s)
    }
}

输出:

&{Name:丽丽 Age:16 Height:162 Weight:33}
&{Name:张三 Age:15 Height:160 Weight:40}
&{Name:苏珊 Age:18 Height:165 Weight:43}
&{Name:李四 Age:18 Height:168 Weight:50}
&{Name:王五 Age:17 Height:170 Weight:50}

上面我们对 Sts 结构体的 Weight 字段进行了排序,如果要对 Age 进行排序,只需要修改 Less 方法:

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

但这样一次只能对结构体一个字段进行排序,如果想同时对多个字段排序我们就要重新改造下。

package main

import (
    "fmt"
    "sort"
)

type Student struct {
    Name   string
    Age    int
    Height int // cm
    Weight int // kg
}

var students = []*Student{
    {"张三", 15, 160, 40},
    {"李四", 18, 168, 50},
    {"苏珊", 18, 165, 43},
    {"丽丽", 16, 162, 33},
    {"王五", 17, 170, 50},
}

type StsSort struct {
    sts  []*Student
    less func(x, y *Student) bool
}

func (s StsSort) Len() int {
    return len(s.sts)
}

func (s StsSort) Less(i, j int) bool {
    return s.less(s.sts[i], s.sts[j])
}

func (s StsSort) Swap(i, j int) {
    s.sts[i], s.sts[j] = s.sts[j], s.sts[i]
}

func main() {
    sort.Sort(&StsSort{students, func(x, y *Student) bool {
        if x.Age != y.Age {
                return x.Age < y.Age
        }
        if x.Height != y.Height {
                return x.Height < y.Height
        }
        if x.Weight != y.Weight {
                return x.Weight < y.Weight
        }
        return false
    }})
    for _, s := range students {
        fmt.Printf("%+v\n", s)
    }
}

输出:

&{Name:张三 Age:15 Height:160 Weight:40}
&{Name:丽丽 Age:16 Height:162 Weight:33}
&{Name:王五 Age:17 Height:170 Weight:50}
&{Name:苏珊 Age:18 Height:165 Weight:43}
&{Name:李四 Age:18 Height:168 Weight:50}

这时可以看到 Age 相同的时候,会按照 Height 排序,前两个字段相同则按照 Weight 排序。