模仿 Go Sort 排序接口實現的自定義排序

查看完整代碼,點擊這裏git

最近在使用 Go 語言實現一些簡單的排序算法時,發現沒法實現一個支持多種類型的排序方法,固然實現一個 int 類型的排序算法是簡單的。例以下面的選擇排序:github

func SelectionSort(arr []int, length int)  {
    for i := 0; i < length; i++ {
        minIndex := i
        for j := i + 1; j < length; j++ {
            if arr[j] < arr[j - 1] {
                minIndex = j
            }
        }
        arr[i], arr[minIndex] = arr[minIndex], arr[i]
    }
}

可是如何實現一個方法,能夠對字母排序、按照結構體內數據排序... 呢?golang

我首先想到的固然是 interface 類型,將 int 替換成 interface 就能夠了嗎?實際是不行的,Go 語言對於類型的要求很是嚴格,由於程序沒法判斷 interface 具體是什麼數據類型,因此它也不知道該怎麼比較一個未知的數據類型,這會致使一個運行時錯誤。算法

interface斷言也是行不通的,由於咱們也不知道會傳入什麼數據類型,又怎麼判斷要斷言爲何數據類型呢。數據結構

而後我就想到了 Go 自帶的 sort 排序包,發現自定義數據類型的排序須要實現對應的接口。如下內容與此文章相似,有部分參考的內容。app

Interface 接口

既然 Go 不知道怎麼排序未知的數據類型,咱們本身來定義比較的規則不就能夠了嗎?Go 的 sort 包正是這樣實現的。學習

// A type, typically a collection, that satisfies sort.Interface can be
// sorted by the routines in this package. The methods require that the
// elements of the collection be enumerated by an integer index.
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)
}

Less() 解決的就是問題的癥結,咱們能夠本身定義比較的邏輯。測試

示例

如下內容是展現如何使用這個接口。ui

自定義排序的結構體

這裏定義了一個學生的數據結構,咱們將對全部的學生進行排序,學生包含他的姓名以及成績,排序的規則是按照學習的成績排序this

type Student struct {
    Name  string
    Score int
}
type Students []Student

實現排序的接口

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

// 在比較的方法中,定義排序的規則
func (s Students) Less(i, j int) bool {
    if s[i].Score < s[j].Score {
        return true
    } else if s[i].Score > s[j].Score {
        return false
    } else {
        return s[i].Name < s[i].Name
    }
}

func (s Students) Swap(i, j int) {
    temp := s[i]
    s[i] = s[j]
    s[j] = temp
}

實現排序邏輯

Go 提供了基於快排實現的排序方法,這裏爲了體驗爲何 Go 這麼定義 Interface 接口,我使用了選擇排序的方法代替 Go 的快排。

func Sort(s sort.Interface) {
    length := s.Len()
    for i := 0; i < length; i++ {
        minIndex := i
        for j := i + 1; j < length; j++ {
            if s.Less(j, i) {
                minIndex = j
            }
        }
        s.Swap(minIndex, i)
    }
}

在這個排序中,我使用了接口中定義的三個方法: Len(),Less(),Swap()。最重要的仍是 Less(),沒有它程序就不知道如何去比較兩個未知元素的大小。

重寫輸出

爲了更好的輸出學生的信息,重寫學生的字符串輸出格式

func (s Student) String() string {
    return fmt.Sprintf("Student: %s %v", s.Name, s.Score)
}

測試輸出

經過如下程序測試咱們的排序算法

func main() {
    arr := []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}
    SelectionSort(arr, len(arr))

    fmt.Println(arr)

    students := student.Students{}

    students = append(students, student.Student{"D", 90})
    students = append(students, student.Student{"C", 100})
    students = append(students, student.Student{"B", 95})
    students = append(students, student.Student{"A", 95})
    Sort(students)

    for _, student := range students {
        fmt.Println(student)
    }
}

如下是輸出結果:

[1 2 3 4 5 6 7 8 9 10]
Student: D 90
Student: A 95
Student: B 95
Student: C 100

總結

查看 sort 包的官方文檔,能夠看到對 Float64intString 的結構體和接口實現,他們的原理跟上文描述其實都是同樣的,只不過是擴展包替咱們預先封裝了這些經常使用的數據類型而已。

實際排序時,優先使用官方提供的 Sort 方法,這裏只是爲了練習才本身實現了一個選擇排序。

相關文章
相關標籤/搜索