排序操作是很多程式經常使用的操作。儘管一個簡短的快排程式只要二三十行程式碼就可以搞定,但是一個健壯的實現需要更多的程式碼,並且我們不希望每次我們需要的時候都重寫或者拷貝這些程式碼。幸運的是,Go內建的sort
包中提供了根據一些排序函式來對任何序列進行排序的功能。
排序整數、浮點數和字串切片
對於[]int
, []float
, []string
這種元素型別是基礎型別的切片使用sort
包提供的下面幾個函式進行排序。
- sort.Ints
- sort.Floats
- sort.Strings
s := []int{4, 2, 3, 1}
sort.Ints(s)
fmt.Println(s) // 輸出[1 2 3 4]
使用自定義比較器排序
- 使用
sort.Slice
函式排序,它使用一個使用者提供的函式來對序列進行排序,函式型別為func(i, j int) bool
,其中引數i
,j
是序列中的索引。 sort.SliceStable
在排序切片時會保留相等元素的原始順序。- 上面兩個函式讓我們可以排序結構體切片(order by struct field value)。
family := []struct {
Name string
Age int
}{
{"Alice", 23},
{"David", 2},
{"Eve", 2},
{"Bob", 25},
}
// 用 age 排序,年齡相等的元素保持原始順序
sort.SliceStable(family, func(i, j int) bool {
return family[i].Age < family[j].Age
})
fmt.Println(family) // [{David 2} {Eve 2} {Alice 23} {Bob 25}]
//下面實現排序order by age asc, name desc,如果 age 和 name 都相等則保持原始排序
sort.SliceStable(family, func(i, j int) bool {
if family[i].Age != family[j].Age {
return family[i].Age < family[j].Age
}
return strings.Compare(family[i].Name, family[j].Name) == 1
})
fmt.Println(family) // [{Eve 2} {David 2} {Alice 23} {Bob 25}]
排序任意資料結構
- 使用
sort.Sort
或者sort.Stable
函式。 - 他們可以排序實現了sort.Interface介面的任意型別
一個內建的排序演算法需要知道三個東西:序列的長度,表示兩個元素比較的結果,一種交換兩個元素的方式;這就是sort.Interface的三個方法:
type Interface interface {
Len() int
Less(i, j int) bool // i, j 是元素的索引
Swap(i, j int)
}
還是以上面的結構體切片為例子,我們為切片型別自定義一個型別名,然後在自定義的型別上實現 srot.Interface 介面
type Person struct {
Name string
Age int
}
// ByAge 通過對age排序實現了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() {
family := []Person{
{"David", 2},
{"Alice", 23},
{"Eve", 2},
{"Bob", 25},
}
sort.Sort(ByAge(family))
fmt.Println(family) // [{David, 2} {Eve 2} {Alice 23} {Bob 25}]
sort.
}
實現了sort.Interface的具體型別不一定是切片型別;下面的customSort是一個結構體型別。
type customSort struct {
p []*Person
less func(x, y *Person) bool
}
func (x customSort) Len() int {len(x.p)}
func (x customSort) Less(i, j int) bool { return x.less(x.p[i], x.p[j]) }
func (x customSort) Swap(i, j int) { x.p[i], x.p[j] = x.p[j], x.p[i] }
讓我們定義一個根據多欄位排序的函式,它主要的排序鍵是Age,Age 相同了再按 Name 進行倒序排序。下面是該排序的呼叫,其中這個排序使用了匿名排序函式:
sort.Sort(customSort{persons, func(x, y *Person) bool {
if x.Age != y.Age {
return x.Age < y.Age
}
if x.Name != y.Name {
return x.Name > y.Name
}
return false
}})
排序具體的演算法和複雜度
Go 的sort
包中所有的排序演算法在最壞的情況下會做 n log n次 比較,n 是被排序序列的長度,所以排序的時間複雜度是 O(n log n*)。其大多數的函式都是用改良後的快速排序演算法實現的。
本作品採用《CC 協議》,轉載必須註明作者和本文連結