真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

排序(sort包)-創(chuàng)新互聯(lián)

使用 sort.Interface 來排序

排序是一個(gè)在很多程序中廣泛使用的操作。sort 包提供了針對(duì)任意序列根據(jù)任意排序函數(shù)原地排序的功能。
這樣的設(shè)計(jì)號(hào)稱并不常見。在很多語言中,排序算法跟序列數(shù)據(jù)類型綁定,排序函數(shù)跟序列元素類型綁定。但 Go 語言的 sort.Sort 函數(shù)對(duì)序列和其中元素的布局無任何要求,它使用 sort.Interface 接口來實(shí)現(xiàn)。

創(chuàng)新互聯(lián)公司成立于2013年,我們提供高端網(wǎng)站建設(shè)公司、成都網(wǎng)站制作成都網(wǎng)站設(shè)計(jì)、網(wǎng)站定制、成都營銷網(wǎng)站建設(shè)、微信平臺(tái)小程序開發(fā)、微信公眾號(hào)開發(fā)、營銷推廣服務(wù),提供專業(yè)營銷思路、內(nèi)容策劃、視覺設(shè)計(jì)、程序開發(fā)來完成項(xiàng)目落地,為成都三維植被網(wǎng)企業(yè)提供源源不斷的流量和訂單咨詢。

接口實(shí)現(xiàn)

一個(gè)原地排序算法需要知道三個(gè)信息:

  1. 序列長度
  2. 比較兩個(gè)元素的含義
  3. 如何交換兩個(gè)元素

所以 sort.Interface 接口就有三個(gè)方法:

package sort

type Interface interface {
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}

字符串排序

要對(duì)序列排序,需要先確定一個(gè)實(shí)現(xiàn)了上面三個(gè)方法的類型,接著把 sort.Sort 函數(shù)應(yīng)用到上面這類方法的示例上。以字符串切片 []string 為例,定義一個(gè)新的類型 StringSlice 以及它的三個(gè)方法如下:

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] }

現(xiàn)在要對(duì)一個(gè)字符串切片進(jìn)行排序,先把類型轉(zhuǎn)換為 StringSlice 類型,然后調(diào)用 sort.Sort 函數(shù)即可:

sort.Sort(StringSlice(names))

字符串切片的排序太常見了,所以 sort 包提供了 StringSLice 類型,以及一個(gè)直接排序的 Strings 函數(shù)。上面對(duì) StringSlice 類型及其方法的定義就是源碼里實(shí)現(xiàn)的代碼。所以要對(duì)字符串切片進(jìn)行排序,直接調(diào)用 srot.Strings 函數(shù)即可:

sort.Strings(names)

為了簡便,sort 包專門對(duì) []int、[]string、[]float64 這三個(gè)類型提供了排序的函數(shù)和相關(guān)類型。對(duì)于其他類型,就需要自己寫了,不過寫起來也不復(fù)雜。

反轉(zhuǎn) Reverse

sort.Reverse 函數(shù)值得仔細(xì)看一下,它使用了一個(gè)重要的概念組合。sort 包定義了一個(gè) reverse 類型,這個(gè)類型是一個(gè)嵌入了 sort.Interface 的結(jié)構(gòu)。reverse 的 Less 方法,直接調(diào)用內(nèi)嵌的 sort.Interface 值的 Less 方法,但是調(diào)換了下標(biāo),這樣就實(shí)現(xiàn)了顛倒排序的結(jié)果了:

package sort

type reverse struct { Interface } // 這個(gè)是在sort包里的,所以就是 sort.Interface
func (r reverse) Less(i, j int) bool { return r.Interface.Less(j, i) }  // 這里調(diào)換了函數(shù)體中 i 和 j 的位置
func Reverse(data Interface) Interface { return &reverse{data} }

reverse 的另外兩個(gè)方法 Len 和 Swap,沒有定義,就會(huì)由內(nèi)嵌的 sort.Interface 隱式提供。導(dǎo)出函數(shù) Reverse 則返回一個(gè)包含原始的 sort.Interface 值的 reverse 實(shí)例。最終反轉(zhuǎn)排序的調(diào)用如下,先做類型轉(zhuǎn)換,然后加一步通過 Reverse 函數(shù)生成 reverse 實(shí)例,最后就是調(diào)用 sort.Sort 函數(shù)完成排序:

sort.Sort(sort.Reverse(StringSlice(names)))

復(fù)雜類型的排序

對(duì)于一個(gè)復(fù)雜的類型,比如結(jié)構(gòu)體,它會(huì)有多個(gè)字段,也就是可能需要對(duì)多個(gè)維度進(jìn)行排序。

數(shù)據(jù)和結(jié)構(gòu)

下面是一個(gè)復(fù)雜的結(jié)構(gòu)體類型,并且數(shù)據(jù)也已經(jīng)準(zhǔn)備好了:

type Track struct {
    Title  string
    Artist string
    Album  string
    Year   int
    Length time.Duration
}

var tracks = []*Track{
    {"Go", "Delilah", "From the Roots Up", 2012, length("3m38s")},
    {"Go", "Moby", "Moby", 1992, length("3m37s")},
    {"Go Ahead", "Alicia Keys", "As I Am", 2007, length("4m36s")},
    {"Ready 2 Go", "Martin Solveig", "Smash", 2011, length("4m24s")},
}

func length(s string) time.Duration {
    d, err := time.ParseDuration(s)
    if err != nil {
        panic(s)
    }
    return d
}

用表格輸出結(jié)構(gòu)體

這里使用 text/tabwriter 包可以生成一個(gè)干凈整潔的表格。這里注意,*tabwriter.Writer 滿足 io.Writer 接口,先用它收集所有寫入的數(shù)據(jù),在 Flush 方法調(diào)用時(shí)才會(huì)格式化整個(gè)表格并且輸出:

func printTracks(tracks []*Track) {
    const format = "%v\t%v\t%v\t%v\t%v\t\n"
    tw := new(tabwriter.Writer).Init(os.Stdout, 0, 8, 2, ' ', 0)
    fmt.Fprintf(tw, format, "Title", "Artist", "Album", "Year", "Length")
    fmt.Fprintf(tw, format, "-----", "------", "-----", "----", "------")
    for _, t := range tracks {
        fmt.Fprintf(tw, format, t.Title, t.Artist, t.Album, t.Year, t.Length)
    }
    tw.Flush()
}

排序

按照 Artist 字段進(jìn)行排序:

type byArtist []*Track

func (x byArtist) Len() int           { return len(x) }
func (x byArtist) Less(i, j int) bool { return x[i].Artist < x[j].Artist }
func (x byArtist) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }

按照 Year 字段進(jìn)行排序:

type byYear []*Track

func (x byYear) Len() int           { return len(x) }
func (x byYear) Less(i, j int) bool { return x[i].Year < x[j].Year }
func (x byYear) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }

優(yōu)化

上面分別對(duì)2個(gè)字段定義了排序。像這樣,對(duì)于每一個(gè)排序都需要實(shí)現(xiàn)一個(gè)新的 sort.Interface。但是其實(shí)只有 Less 方法不同,而 Len 和 Swap 方法都是一樣的。
在下面的例子中,具體類型 customSort 組合了一個(gè)要排序的切片類型和一個(gè)函數(shù),這樣每次只要寫一個(gè)比較函數(shù)就可以定義一個(gè)新的排序。從這個(gè)例子也可以看到,實(shí)現(xiàn) sort.Interface 的具體類型并不一定都是切片,比如這里的 customSort 就是一個(gè)結(jié)構(gòu)體:

type customSort struct {
    t    []*Track
    less func(x, y *Track) bool
}

func (x customSort) Len() int           { return len(x.t) }
func (x customSort) Less(i, j int) bool { return x.less(x.t[i], x.t[j]) }
func (x customSort) Swap(i, j int)      { x.t[i], x.t[j] = x.t[j], x.t[i] }

接下來就定義一個(gè)多層的比較函數(shù),先對(duì) Title 排序,然后對(duì) Year,最后是時(shí)長:

sort.Sort(customSort{tracks, func(x, y *Track) bool {
    if x.Title != y.Title {
        return x.Title < y.Title
    }
    if x.Year != y.Year {
        return x.Year < y.Year
    }
    if x.Length != y.Length {
        return x.Length < y.Length
    }
    return false
}})

檢查是否有序

一個(gè)長度為 n 的序列進(jìn)行排序需要 O(n logn) 次比較操作,而判斷一個(gè)序列是否已經(jīng)排好序值需要最多 (n-1) 次比較。sort 包提供的 IsSorted 函數(shù)可以判斷序列是否是排好序的。

values := []int{3, 1, 4, 1}
fmt.Println(sort.IntsAreSorted(values))                         // "false"
sort.Ints(values)                                               // 排序
fmt.Println(values)                                             // "[1 1 3 4]"
fmt.Println(sort.IntsAreSorted(values))                         // "true"
sort.Sort(sort.Reverse(sort.IntSlice(values)))                  // 反轉(zhuǎn)
fmt.Println(values)                                             // "[4 3 1 1]"
fmt.Println(sort.IntsAreSorted(values))                         // "false"
fmt.Println(sort.IsSorted(sort.Reverse(sort.IntSlice(values)))) // "true"

完整示例代碼

上面的代碼的源碼文件,可以運(yùn)行:

package main

import (
    "fmt"
    "os"
    "sort"
    "text/tabwriter"
    "time"
)

type Track struct {
    Title  string
    Artist string
    Album  string
    Year   int
    Length time.Duration
}

var tracks = []*Track{
    {"Go", "Delilah", "From the Roots Up", 2012, length("3m38s")},
    {"Go", "Moby", "Moby", 1992, length("3m37s")},
    {"Go Ahead", "Alicia Keys", "As I Am", 2007, length("4m36s")},
    {"Ready 2 Go", "Martin Solveig", "Smash", 2011, length("4m24s")},
}

func length(s string) time.Duration {
    d, err := time.ParseDuration(s)
    if err != nil {
        panic(s)
    }
    return d
}

func printTracks(tracks []*Track) {
    const format = "%v\t%v\t%v\t%v\t%v\t\n"
    tw := new(tabwriter.Writer).Init(os.Stdout, 0, 8, 2, ' ', 0)
    fmt.Fprintf(tw, format, "Title", "Artist", "Album", "Year", "Length")
    fmt.Fprintf(tw, format, "-----", "------", "-----", "----", "------")
    for _, t := range tracks {
        fmt.Fprintf(tw, format, t.Title, t.Artist, t.Album, t.Year, t.Length)
    }
    tw.Flush()
}

type byArtist []*Track

func (x byArtist) Len() int           { return len(x) }
func (x byArtist) Less(i, j int) bool { return x[i].Artist < x[j].Artist }
func (x byArtist) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }

type byYear []*Track

func (x byYear) Len() int           { return len(x) }
func (x byYear) Less(i, j int) bool { return x[i].Year < x[j].Year }
func (x byYear) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }

func main() {
    fmt.Println("byArtist:")
    sort.Sort(byArtist(tracks))
    printTracks(tracks)

    fmt.Println("\nReverse(byArtist):")
    sort.Sort(sort.Reverse(byArtist(tracks)))
    printTracks(tracks)

    fmt.Println("\nbyYear:")
    sort.Sort(byYear(tracks))
    printTracks(tracks)

    fmt.Println("\nCustom:")
    sort.Sort(customSort{tracks, func(x, y *Track) bool {
        if x.Title != y.Title {
            return x.Title < y.Title
        }
        if x.Year != y.Year {
            return x.Year < y.Year
        }
        if x.Length != y.Length {
            return x.Length < y.Length
        }
        return false
    }})
    printTracks(tracks)
}

type customSort struct {
    t    []*Track
    less func(x, y *Track) bool
}

func (x customSort) Len() int           { return len(x.t) }
func (x customSort) Less(i, j int) bool { return x.less(x.t[i], x.t[j]) }
func (x customSort) Swap(i, j int)      { x.t[i], x.t[j] = x.t[j], x.t[i] }

func init() {
    values := []int{3, 1, 4, 1}
    fmt.Println(sort.IntsAreSorted(values))                         // "false"
    sort.Ints(values)                                               // 排序
    fmt.Println(values)                                             // "[1 1 3 4]"
    fmt.Println(sort.IntsAreSorted(values))                         // "true"
    sort.Sort(sort.Reverse(sort.IntSlice(values)))                  // 反轉(zhuǎn)
    fmt.Println(values)                                             // "[4 3 1 1]"
    fmt.Println(sort.IntsAreSorted(values))                         // "false"
    fmt.Println(sort.IsSorted(sort.Reverse(sort.IntSlice(values)))) // "true"
}

穩(wěn)定的排序

sort.Sort 是不穩(wěn)定的排序。在“優(yōu)化”章節(jié)里的例子,先對(duì)Title進(jìn)行比較,然后是Year,再是Length,實(shí)現(xiàn)了多個(gè)字段的排序。在第一關(guān)鍵字相等的情況下,再通過第二關(guān)鍵字進(jìn)行比較。整個(gè)過程只進(jìn)行了一次序列的交換,這樣看不出問題。
如果對(duì)多個(gè)關(guān)鍵字進(jìn)行排序,但是不是像上面這樣通過一次比較交換完成,而是先對(duì)一個(gè)關(guān)鍵字進(jìn)行排序,得到一個(gè)已經(jīng)有序的序列。然后再在這個(gè)序列的基礎(chǔ)上,再進(jìn)行一次對(duì)另一個(gè)關(guān)鍵字的排序,穩(wěn)定的排序和不穩(wěn)定的排序的結(jié)果就會(huì)有差別。比如排序第一關(guān)鍵字是年齡,第二關(guān)鍵字是姓名。那么先做一個(gè)姓名的排序(穩(wěn)定不穩(wěn)定無所謂),然后再在原來的基礎(chǔ)上做一次年齡的排序(必須穩(wěn)定),才能得到正確的結(jié)果。下面通過具體事例來說明,穩(wěn)定排序和不穩(wěn)定排序的差別。

準(zhǔn)備數(shù)據(jù)

數(shù)據(jù)結(jié)構(gòu)、打印的函數(shù)、具體數(shù)據(jù)如下代碼實(shí)現(xiàn):

type Student struct {
    name string
    age  int
}

var students []*Student

func printStudents(students []*Student) {
    const format = "%v\t%v\t\n"
    tw := new(tabwriter.Writer).Init(os.Stdout, 0, 8, 2, ' ', 0)
    fmt.Fprintf(tw, format, "name", "age")
    fmt.Fprintf(tw, format, "----", "---")
    for _, s := range students {
        fmt.Fprintf(tw, format, s.name, s.age)
    }
    tw.Flush()
}

func init() {
    students = []*Student{
        &Student{"Adam", 20},
        &Student{"Bob", 18},
        &Student{"Clark", 19},
        &Student{"Daisy", 18},
        &Student{"Eva", 20},
        &Student{"Frank", 20},
        &Student{"Gideon", 19},
    }
}

接口實(shí)現(xiàn)

這里使用通用的 less 方法:

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

func (x studentSort) Len() int           { return len(x.s) }
func (x studentSort) Less(i, j int) bool { return x.less(x.s[i], x.s[j]) }
func (x studentSort) Swap(i, j int)      { x.s[i], x.s[j] = x.s[j], x.s[i] }

不穩(wěn)定的排序

先對(duì) name 進(jìn)行排序,然后再原來序列的基礎(chǔ)上對(duì) age 進(jìn)行排序:

func main() {
    sort.Sort(studentSort{students, func(x, y *Student) bool {
        if x.name != y.name {
            return x.name < y.name
        }
        return false
    }})

    sort.Sort(studentSort{students, func(x, y *Student) bool {
        if x.age != y.age {
            return x.age < y.age
        }
        return false
    }})
    printStudents(students)
}

最后打印的結(jié)果是這樣的:

PS H:\Go\src\gopl\output\sort\stable> go run main.go
name    age
----    ---
Bob     18
Daisy   18
Gideon  19
Clark   19
Eva     20
Frank   20
Adam    20
PS H:\Go\src\gopl\output\sort\stable>

在這個(gè)結(jié)果里年齡相同的數(shù)據(jù),并沒有按字母順序排序,雖然之前已經(jīng)按字母順序進(jìn)行了排序,不過在第二次排序的時(shí)候,會(huì)把之前的順序打亂,這就是不穩(wěn)定的排序。

穩(wěn)定的排序

這里來看看穩(wěn)定的排序。這里只要把 sort.Sort 函數(shù)替換成 sort.Stable 即可:

func main() {
    sort.Sort(studentSort{students, func(x, y *Student) bool {
        if x.name != y.name {
            return x.name < y.name
        }
        return false
    }})

    sort.Stable(studentSort{students, func(x, y *Student) bool {
        if x.age != y.age {
            return x.age < y.age
        }
        return false
    }})
    printStudents(students)
}

這里只替換了第二次排序的函數(shù)。第一次排序的時(shí)候假設(shè)序列本來就是亂的,所以這里并沒有需要穩(wěn)定的必要。
這次再來看下結(jié)果:

PS H:\Go\src\gopl\output\sort\stable> go run main.go
name    age
----    ---
Bob     18
Daisy   18
Clark   19
Gideon  19
Adam    20
Eva     20
Frank   20
PS H:\Go\src\gopl\output\sort\stable>

這次的結(jié)果是期望的樣子了,按年齡排序,如果年齡相同,再按字母順序排序。

小結(jié)

sort.Stable 雖然能保證排序的穩(wěn)定性,但是犧牲了效率。如果需要保證序列的穩(wěn)定性,那么就要用 sort.Stable 函數(shù)來代替 sort.Sort。
但是如果僅僅只是要對(duì)多個(gè)字段進(jìn)行排序,也可以直接在比較的時(shí)候就完成全部字段的比較,僅做一次序列的調(diào)換(就是排序)。就向上面“優(yōu)化”章節(jié)里的 customSort 實(shí)現(xiàn)的那樣,下面是這里例子里的實(shí)現(xiàn):

func main() {
    sort.Sort(studentSort{students, func(x, y *Student) bool {
        if x.age != y.age {
            return x.age < y.age
        }
        if x.name != y.name {
            return x.name < y.name
        }
        return false
    }})
    printStudents(students)
}

創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動(dòng)態(tài)BGP最優(yōu)骨干路由自動(dòng)選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動(dòng)現(xiàn)已開啟,新人活動(dòng)云服務(wù)器買多久送多久。


分享名稱:排序(sort包)-創(chuàng)新互聯(lián)
鏈接分享:http://weahome.cn/article/eighd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部