golang sort —— 排序算法


該包實現了四種基本排序算法:插入排序、歸並排序、堆排序和快速排序。 但是這四種排序方法是不公開的,它們只被用於sort包內部使用。所以在對數據集合排序時不必考慮應當選擇哪一種排序方法,只要實現了sort.Interface定義的三個方法:獲取數據集合長度的Len()方法、比較兩個元素大小的Less()方法和交換兩個元素位置的Swap()方法,就可以順利對數據集合進行排序。sort包會根據實際數據自動選擇高效的排序算法。 除此之外,為了方便對常用數據類型的操作,sort包提供了對[]int切片、[]float64切片和[]string切片完整支持,主要包括:

對基本數據類型切片的排序支持
基本數據元素查找
判斷基本數據類型切片是否已經排好序
對排好序的數據集合逆序

3.1.1 數據集合排序

前面已經提到過,對數據集合(包括自定義數據類型的集合)排序需要實現sort.Interface接口的三個方法,我們看以下該接口的定義:

type Interface interface {
		// 獲取數據集合元素個數
		Len() int
		// 如果i索引的數據小於j所以的數據,返回true,不會調用
		// 下面的Swap(),即數據升序排序。
		Less(i, j int) bool
		// 交換i和j索引的兩個元素的位置
        Swap(i, j int)
}

數據集合實現了這三個方法后,即可調用該包的Sort()方法進行排序。 Sort()方法定義如下:

`func Sort(data Interface)`

Sort()方法惟一的參數就是待排序的數據集合。

該包還提供了一個方法可以判斷數據集合是否已經排好順序,該方法的內部實現依賴於我們自己實現的Len()和Less()方法:

func IsSorted(data Interface) bool {
    n := data.Len()
    for i := n - 1; i > 0; i-- {
        if data.Less(i, i-1) {
            return false
        }
    }
    return true
}

下面是一個使用sort包對學生成績排序的示例:

package main

import (
	"fmt"
	"sort"
)

//學生成績結構體
type StuScore struct {
     //姓名
    name  string
    //成績
    score int
}

type StuScores []StuScore

//Len()
func (s StuScores) Len() int {
	return len(s)
}

//Less():成績將有低到高排序
func (s StuScores) Less(i, j int) bool {
	return s[i].score < s[j].score
}

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

func main() {
    stus := StuScores{
                {"alan", 95},
                {"hikerell", 91},
                {"acmfly", 96},
                {"leao", 90}}

    fmt.Println("Default:")
    //原始順序
    for _, v := range stus {
        fmt.Println(v.name, ":", v.score)
    }
    fmt.Println()
    //StuScores已經實現了sort.Interface接口
    sort.Sort(stus)
    
    fmt.Println("Sorted:")
     //排好序后的結構
    for _, v := range stus {
        fmt.Println(v.name, ":", v.score)
    }

    //判斷是否已經排好順序,將會打印true
    fmt.Println("IS Sorted?", sort.IsSorted(stus))
}

程序該示例程序的自定義類型StuScores實現了sort.Interface接口,所以可以將其對象作為sort.Sort()和sort.IsSorted()的參數傳入。運行結果:

======Default======
alan : 95
hikerell : 91
acmfly : 96
leao : 90

======Sorted=======
leao : 90
hikerell : 91
alan : 95
acmfly : 96
IS Sorted? true

該示例實現的是升序排序,如果要得到降序排序結果,其實只要修改Less()函數:

//Less():成績降序排序,只將小於號修改為大於號
func (s StuScores) Less(i, j int) bool {
	return s[i].score > s[j].score
}

此外,sort包提供了Reverse()方法,可以允許將數據按Less()定義的排序方式逆序排序,而不必修改Less()代碼。方法定義如下:

func Reverse(data Interface) Interface

我們可以看到Reverse()返回的一個sort.Interface接口類型,整個Reverse()的內部實現比較有趣:

    //定義了一個reverse結構類型,嵌入Interface接口
    type reverse struct {
        Interface
    }

    //reverse結構類型的Less()方法擁有嵌入的Less()方法相反的行為
    //Len()和Swap()方法則會保持嵌入類型的方法行為
    func (r reverse) Less(i, j int) bool {
        return r.Interface.Less(j, i)
    }

    //返回新的實現Interface接口的數據類型
    func Reverse(data Interface) Interface {
        return &reverse{data}
    }

了解內部原理后,可以在學生成績排序示例中使用Reverse()來實現成績升序排序:

    sort.Sort(sort.Reverse(stus))
     for _, v := range stus {
        fmt.Println(v.name, ":", v.score)
    }

最后一個方法:Search()

func Search(n int, f func(int) bool) int

官方文檔這樣描述該方法:

Search()方法回使用“二分查找”算法來搜索某指定切片[0:n],並返回能夠使f(i)=true的最 小的i(0<=i<n)值,並且會假定,如果f(i)=true,則f(i+1)=true,即對於切片[0:n],
i之前的切片元素會使f()函數返回false,i及i之后的元素會使f()函數返回true。但是,當 在切片中無法找到時f(i)=true的i時(此時切片元素都不能使f()函數返回true),Search() 方法會返回n。

Search()函數一個常用的使用方式是搜索元素x是否在已經升序排好的切片s中:

    x := 11
    s := []int{3, 6, 8, 11, 45} //注意已經升序排序
    pos := sort.Search(len(s), func(i int) bool { return s[i] >= x })
    if pos < len(s) && s[pos] == x {
        fmt.Println(x, "在s中的位置為:", pos)
    } else {
        fmt.Println("s不包含元素", x)
    }

官方文檔還給出了一個猜數字的小程序:

func GuessingGame() {
	var s string
	fmt.Printf("Pick an integer from 0 to 100.\n")
	answer := sort.Search(100, func(i int) bool {
		fmt.Printf("Is your number <= %d? ", i)
		fmt.Scanf("%s", &s)
		return s != "" && s[0] == 'y'
	})
	fmt.Printf("Your number is %d.\n", answer)
}

3.1.2 sort包已經支持的內部數據類型排序

前面已經提到,sort包原生支持[]int、[]float64和[]string三種內建數據類型切片的排序操作,即不必我們自己實現相關的Len()、Less()和Swap()方法。

  1. IntSlice類型及[]int排序

由於[]int切片排序內部實現及使用方法與[]float64和[]string類似,所以只詳細描述該部分。

sort包定義了一個IntSlice類型,並且實現了sort.Interface接口:

    type IntSlice []int
    func (p IntSlice) Len() int           { return len(p) }
    func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }
    func (p IntSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
    //IntSlice類型定義了Sort()方法,包裝了sort.Sort()函數
    func (p IntSlice) Sort() { Sort(p) }
    //IntSlice類型定義了SearchInts()方法,包裝了SearchInts()函數
    func (p IntSlice) Search(x int) int { return SearchInts(p, x) }

並且提供的sort.Ints()方法使用了該IntSlice類型:

`func Ints(a []int) { Sort(IntSlice(a)) }`

所以,對[]int切片排序是更常使用sort.Ints(),而不是直接使用IntSlice類型:

    s := []int{5, 2, 6, 3, 1, 4} // 未排序的切片數據
    sort.Ints(s)
    fmt.Println(s) //將會輸出[1 2 3 4 5 6]

如果要使用降序排序,顯然要用前面提到的Reverse()方法:

    s := []int{5, 2, 6, 3, 1, 4} // 未排序的切片數據
    sort.Sort(sort.Reverse(sort.IntSlice(s)))
    fmt.Println(s) //將會輸出[6 5 4 3 2 1]

如果要查找整數x在切片a中的位置,相對於前面提到的Search()方法,sort包提供了SearchInts():

`func SearchInts(a []int, x int) int`

注意,SearchInts()的使用條件為:切片a已經升序排序

    s := []int{5, 2, 6, 3, 1, 4} // 未排序的切片數據
    sort.Ints(s) //排序后的s為[1 2 3 4 5 6]
    fmt.Println(sort.SearchInts(s, 3)) //將會輸出2
  1. Float64Slice類型及[]float64排序

實現與Ints類似,只看一下其內部實現:

    type Float64Slice []float64

    func (p Float64Slice) Len() int           { return len(p) }
    func (p Float64Slice) Less(i, j int) bool { return p[i] < p[j] || isNaN(p[i]) && !isNaN(p[j]) }
    func (p Float64Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
    func (p Float64Slice) Sort() { Sort(p) }
    func (p Float64Slice) Search(x float64) int { return SearchFloat64s(p, x) }

與Sort()、IsSorted()、Search()相對應的三個方法:

    func Float64s(a []float64)  
    func Float64sAreSorted(a []float64) bool
    func SearchFloat64s(a []float64, x float64) int

要說明一下的是,在上面Float64Slice類型定義的Less方法中,有一個內部函數isNaN()。 isNaN()與math包中IsNaN()實現完全相同,sort包之所以不使用math.IsNaN(),完全是基於包依賴性的考慮,應當看到,sort包的實現不依賴與其他任何包。

  1. StringSlice類型及[]string排序

兩個string對象之間的大小比較是基於“字典序”的。

實現與Ints類似,只看一下其內部實現:

    type StringSlice []string
    
    func (p StringSlice) Len() int           { return len(p) }
    func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] }
    func (p StringSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
    func (p StringSlice) Sort() { Sort(p) }
    func (p StringSlice) Search(x string) int { return SearchStrings(p, x) }

與Sort()、IsSorted()、Search()相對應的三個方法:

    func Strings(a []string)
    func StringsAreSorted(a []string) bool
    func SearchStrings(a []string, x string) int


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM