亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解go語(yǔ)言中sort如何排序

 更新時(shí)間:2022年03月07日 09:07:58   作者:Zhan-LiZ  
我們的代碼業(yè)務(wù)中很多地方需要我們自己進(jìn)行排序操作,本文主要介紹了詳解go語(yǔ)言中sort如何排序,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

sort 包源碼解讀

前言

我們的代碼業(yè)務(wù)中很多地方需要我們自己進(jìn)行排序操作,go 標(biāo)準(zhǔn)庫(kù)中是提供了 sort 包是實(shí)現(xiàn)排序功能的,這里來(lái)看下生產(chǎn)級(jí)別的排序功能是如何實(shí)現(xiàn)的。

go version go1.16.13 darwin/amd64

如何使用

先來(lái)看下 sort 提供的主要功能

  • 對(duì)基本數(shù)據(jù)類型切片的排序支持
  • 自定義 Less 排序比較器
  • 自定義數(shù)據(jù)結(jié)構(gòu)的排序
  • 判斷基本數(shù)據(jù)類型切片是否已經(jīng)排好序
  • 基本數(shù)據(jù)元素查找

基本數(shù)據(jù)類型切片的排序

sort 包中已經(jīng)實(shí)現(xiàn)了對(duì) []int, []float, []string 這幾種類型的排序

func TestSort(t *testing.T) {
?? ?s := []int{5, 2, 6, 3, 1, 4}
?? ?fmt.Println("是否排好序了", sort.IntsAreSorted(s))
?? ?sort.Ints(s)
?? ?// 正序
?? ?fmt.Println(s)
?? ?// 倒序
?? ?sort.Sort(sort.Reverse(sort.IntSlice(s)))
?? ?fmt.Println(s)
?? ?// 穩(wěn)定排序
?? ?sort.Stable(sort.IntSlice(s))
?? ?fmt.Println("是否排好序了", sort.IntsAreSorted(s))
?? ?fmt.Println("查找是否存在", sort.SearchInts(s, 5))
?? ?fmt.Println(s)

?? ?str := []string{"s", "f", "d", "c", "r", "a"}
?? ?sort.Strings(str)
?? ?fmt.Println(str)

?? ?flo := []float64{1.33, 4.78, 0.11, 6.77, 8.99, 4.22}
?? ?sort.Float64s(flo)
?? ?fmt.Println(flo)
}

看下輸出

是否排好序了 false
[1 2 3 4 5 6]
[6 5 4 3 2 1]
是否排好序了 true
查找是否存在 4
[1 2 3 4 5 6]
[a c d f r s]
[0.11 1.33 4.22 4.78 6.77 8.99]

sort 本身不是穩(wěn)定排序,需要穩(wěn)定排序使用sort.Stable,同時(shí)排序默認(rèn)是升序,降序可使用sort.Reverse

自定義 Less 排序比較器

如果我們需要進(jìn)行的排序的內(nèi)容是一些復(fù)雜的結(jié)構(gòu),例如下面的栗子,是個(gè)結(jié)構(gòu)體,根據(jù)結(jié)構(gòu)體中的某一個(gè)屬性進(jìn)行排序,這時(shí)候可以通過(guò)自定義 Less 比較器實(shí)現(xiàn)

使用 sort.Slice,sort.Slice中提供了 less 函數(shù),我們,可以自定義這個(gè)函數(shù),然后通過(guò)sort.Slice進(jìn)行排序,sort.Slice不是穩(wěn)定排序,穩(wěn)定排序可使用sort.SliceStable

type Person struct {
?? ?Name string
?? ?Age ?int
}

func TestSortSlice(t *testing.T) {
?? ?people := []Person{
?? ??? ?{"Bob", 31},
?? ??? ?{"John", 42},
?? ??? ?{"Michael", 17},
?? ??? ?{"Jenny", 26},
?? ?}

?? ?sort.Slice(people, func(i, j int) bool {
?? ??? ?return people[i].Age < people[j].Age
?? ?})
?? ?// Age正序
?? ?fmt.Println(people)
?? ?// Age倒序
?? ?sort.Slice(people, func(i, j int) bool {
?? ??? ?return people[i].Age > people[j].Age
?? ?})
?? ?fmt.Println(people)

?? ?// 穩(wěn)定排序
?? ?sort.SliceStable(people, func(i, j int) bool {
?? ??? ?return people[i].Age > people[j].Age
?? ?})
?? ?fmt.Println(people)
}

看下輸出

[{Michael 17} {Jenny 26} {Bob 31} {John 42}]
[{John 42} {Bob 31} {Jenny 26} {Michael 17}]
[{John 42} {Bob 31} {Jenny 26} {Michael 17}]

自定義數(shù)據(jù)結(jié)構(gòu)的排序

對(duì)自定義結(jié)構(gòu)的排序,除了可以自定義 Less 排序比較器之外,sort 包中也提供了sort.Interface接口,我們只要實(shí)現(xiàn)了sort.Interface中提供的三個(gè)方法,即可通過(guò) sort 包內(nèi)的函數(shù)完成排序,查找等操作

// An implementation of Interface can be sorted by the routines in this package.
// The methods refer to elements of the underlying collection by integer index.
type Interface interface {
?? ?// Len is the number of elements in the collection.
?? ?Len() int

?? ?// Less reports whether the element with index i
?? ?// must sort before the element with index j.
?? ?//
?? ?// If both Less(i, j) and Less(j, i) are false,
?? ?// then the elements at index i and j are considered equal.
?? ?// Sort may place equal elements in any order in the final result,
?? ?// while Stable preserves the original input order of equal elements.
?? ?//
?? ?// Less must describe a transitive ordering:
?? ?// ?- if both Less(i, j) and Less(j, k) are true, then Less(i, k) must be true as well.
?? ?// ?- if both Less(i, j) and Less(j, k) are false, then Less(i, k) must be false as well.
?? ?//
?? ?// Note that floating-point comparison (the < operator on float32 or float64 values)
?? ?// is not a transitive ordering when not-a-number (NaN) values are involved.
?? ?// See Float64Slice.Less for a correct implementation for floating-point values.
?? ?Less(i, j int) bool

?? ?// Swap swaps the elements with indexes i and j.
?? ?Swap(i, j int)
}

來(lái)看下如何使用

type ByAge []Person

func (a ByAge) Len() int ? ? ? ? ? { return len(a) }
func (a ByAge) Swap(i, j int) ? ? ?{ a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }

func TestSortStruct(t *testing.T) {
?? ?people := []Person{
?? ??? ?{"Bob", 31},
?? ??? ?{"John", 42},
?? ??? ?{"Michael", 17},
?? ??? ?{"Jenny", 26},
?? ?}

?? ?sort.Sort(ByAge(people))
?? ?fmt.Println(people)
}

輸出

[{Michael 17} {Jenny 26} {Bob 31} {John 42}]

當(dāng)然 sort 包中已經(jīng)實(shí)現(xiàn)的[]int, []float, []string 這幾種類型的排序也是實(shí)現(xiàn)了sort.Interface接口

對(duì)于上面的三種排序,第一種和第二種基本上就能滿足我們的額需求了,不過(guò)第三種靈活性更強(qiáng)。

分析下源碼

先來(lái)看下什么是穩(wěn)定性排序

栗如:對(duì)一個(gè)數(shù)組進(jìn)行排序,如果里面有重復(fù)的數(shù)據(jù),排完序時(shí)候,相同的數(shù)據(jù)的相對(duì)索引位置沒(méi)有發(fā)生改變,那么就是穩(wěn)定排序。

也就是里面有兩個(gè)5,5。排完之后第一個(gè)5還在最前面,沒(méi)有和后面的重復(fù)數(shù)據(jù)5發(fā)生過(guò)位置的互換,那么這就是穩(wěn)定排序。

不穩(wěn)定排序

sort 中的排序算法用到了,quickSort(快排),heapSort(堆排序),insertionSort(插入排序),shellSort(希爾排序)

先來(lái)分析下這幾種排序算法的使用

可以看下調(diào)用 Sort 進(jìn)行排序,最終都會(huì)調(diào)用 quickSort

func Sort(data Interface) {
?? ?n := data.Len()
?? ?quickSort(data, 0, n, maxDepth(n))
}

再來(lái)看下 quickSort 的實(shí)現(xiàn)

func quickSort(data Interface, a, b, maxDepth int) {
?? ?// 切片長(zhǎng)度大于12的時(shí)候使用快排
?? ?for b-a > 12 { // Use ShellSort for slices <= 12 elements
?? ??? ?// maxDepth 返回快速排序應(yīng)該切換的閾值
?? ??? ?// 進(jìn)行堆排序
?? ??? ?// 當(dāng) maxDepth為0的時(shí)候進(jìn)行堆排序
?? ??? ?if maxDepth == 0 {
?? ??? ??? ?heapSort(data, a, b)
?? ??? ??? ?return
?? ??? ?}
?? ??? ?maxDepth--
?? ??? ?// doPivot 是快排核心算法,它取一點(diǎn)為軸,把不大于軸的元素放左邊,大于軸的元素放右邊,返回小于軸部分?jǐn)?shù)據(jù)的最后一個(gè)下標(biāo),以及大于軸部分?jǐn)?shù)據(jù)的第一個(gè)下標(biāo)
?? ??? ?// 下標(biāo)位置 a...mlo,pivot,mhi...b
?? ??? ?// data[a...mlo] <= data[pivot]
?? ??? ?// data[mhi...b] > data[pivot]
?? ??? ?// 和中位數(shù)一樣的數(shù)據(jù)就不用在進(jìn)行交換了,維護(hù)這個(gè)范圍值能減少數(shù)據(jù)的次數(shù) ?
?? ??? ?mlo, mhi := doPivot(data, a, b)
?? ??? ?// 避免遞歸過(guò)深
?? ??? ?// 循環(huán)是比遞歸節(jié)省時(shí)間的,如果有大規(guī)模的子節(jié)點(diǎn),讓小的先遞歸,達(dá)到了 maxDepth 也就是可以觸發(fā)堆排序的條件了,然后使用堆排序進(jìn)行排序
?? ??? ?if mlo-a < b-mhi {
?? ??? ??? ?quickSort(data, a, mlo, maxDepth)
?? ??? ??? ?a = mhi // i.e., quickSort(data, mhi, b)
?? ??? ?} else {
?? ??? ??? ?quickSort(data, mhi, b, maxDepth)
?? ??? ??? ?b = mlo // i.e., quickSort(data, a, mlo)
?? ??? ?}
?? ?}
?? ?// 如果切片的長(zhǎng)度大于1小于等于12的時(shí)候,使用 shell 排序 ?
?? ?if b-a > 1 {
?? ??? ?// Do ShellSort pass with gap 6
?? ??? ?// It could be written in this simplified form cause b-a <= 12
?? ??? ?// 這里先做一輪shell 排序
?? ??? ?for i := a + 6; i < b; i++ {
?? ??? ??? ?if data.Less(i, i-6) {
?? ??? ??? ??? ?data.Swap(i, i-6)
?? ??? ??? ?}
?? ??? ?}
?? ??? ?// 進(jìn)行插入排序
?? ??? ?insertionSort(data, a, b)
?? ?}
}

// maxDepth 返回快速排序應(yīng)該切換的閾值
// 進(jìn)行堆排序
func maxDepth(n int) int {
?? ?var depth int
?? ?for i := n; i > 0; i >>= 1 {
?? ??? ?depth++
?? ?}
?? ?return depth * 2
}

// doPivot 是快排核心算法,它取一點(diǎn)為軸,把不大于軸的元素放左邊,大于軸的元素放右邊,返回小于軸部分?jǐn)?shù)據(jù)的最后一個(gè)下標(biāo),以及大于軸部分?jǐn)?shù)據(jù)的第一個(gè)下標(biāo)
// 下標(biāo)位置 lo...midlo,pivot,midhi...hi
// data[lo...midlo] <= data[pivot]
// data[midhi...hi] > data[pivot]
func doPivot(data Interface, lo, hi int) (midlo, midhi int) {
?? ?m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow.
?? ?// 這里用到了 Tukey's ninther 算法,文章鏈接 https://www.johndcook.com/blog/2009/06/23/tukey-median-ninther/
?? ?// 通過(guò)該算法求出中位數(shù)
?? ?if hi-lo > 40 {
?? ??? ?// Tukey's ``Ninther,'' median of three medians of three.
?? ??? ?s := (hi - lo) / 8
?? ??? ?medianOfThree(data, lo, lo+s, lo+2*s)
?? ??? ?medianOfThree(data, m, m-s, m+s)
?? ??? ?medianOfThree(data, hi-1, hi-1-s, hi-1-2*s)
?? ?}

?? ?// 求出中位數(shù) data[m] <= data[lo] <= data[hi-1]
?? ?medianOfThree(data, lo, m, hi-1)

?? ?// Invariants are:
?? ?//?? ?data[lo] = pivot (set up by ChoosePivot)
?? ?//?? ?data[lo < i < a] < pivot
?? ?//?? ?data[a <= i < b] <= pivot
?? ?//?? ?data[b <= i < c] unexamined
?? ?//?? ?data[c <= i < hi-1] > pivot
?? ?//?? ?data[hi-1] >= pivot
?? ?// 中位數(shù)
?? ?pivot := lo
?? ?a, c := lo+1, hi-1

?? ?// 處理使 data[lo < i < a] < pivot
?? ?for ; a < c && data.Less(a, pivot); a++ {
?? ?}
?? ?b := a
?? ?for {
?? ??? ?// 處理使 data[a <= i < b] <= pivot
?? ??? ?for ; b < c && !data.Less(pivot, b); b++ {
?? ??? ?}
?? ??? ?// 處理使 data[c <= i < hi-1] > pivot
?? ??? ?for ; b < c && data.Less(pivot, c-1); c-- { // data[c-1] > pivot
?? ??? ?}
?? ??? ?// 左邊和右邊重合或者已經(jīng)在右邊的右側(cè)
?? ??? ?if b >= c {
?? ??? ??? ?break
?? ??? ?}
?? ??? ?// data[b] > pivot; data[c-1] <= pivot
?? ??? ?// 左側(cè)的數(shù)據(jù)大于右側(cè),交換,然后接著排序
?? ??? ?data.Swap(b, c-1)
?? ??? ?b++
?? ??? ?c--
?? ?}
?? ?// If hi-c<3 then there are duplicates (by property of median of nine).
?? ?// Let's be a bit more conservative, and set border to 5.
?? ?// 如果 hi-c<3 則存在重復(fù)項(xiàng)(按中位數(shù)為 9 的屬性)。
?? ?// 讓我們稍微保守一點(diǎn),將邊框設(shè)置為 5。

?? ?// 因?yàn)閏為劃分pivot的大小的臨界值,所以在9值劃分時(shí),正常來(lái)說(shuō),應(yīng)該是兩邊各4個(gè)
?? ?// 由于左邊是<=,多了個(gè)相等的情況,所以5,3分布,也是沒(méi)有問(wèn)題
?? ?// 如果hi-c<3,c的值明顯偏向于hi,說(shuō)明有多個(gè)和pivot重復(fù)值
?? ?// 為了更保守一點(diǎn),所以設(shè)置為5(反正只是多校驗(yàn)一次而已)
?? ?protect := hi-c < 5
?? ?// 即便大于等于5,也可能是因?yàn)樵乜傊岛芏?,所以?duì)比hi-c是否小于總數(shù)量的1/4
?? ?if !protect && hi-c < (hi-lo)/4 {
?? ??? ?// 用一些特殊的點(diǎn)和中間數(shù)進(jìn)行比較
?? ??? ?dups := 0
?? ??? ?// 處理使 data[hi-1] = pivot
?? ??? ?if !data.Less(pivot, hi-1) {
?? ??? ??? ?data.Swap(c, hi-1)
?? ??? ??? ?c++
?? ??? ??? ?dups++
?? ??? ?}
?? ??? ?// 處理使 data[b-1] = pivot
?? ??? ?if !data.Less(b-1, pivot) {
?? ??? ??? ?b--
?? ??? ??? ?dups++
?? ??? ?}
?? ??? ?// m-lo = (hi-lo)/2 > 6
?? ??? ?// b-lo > (hi-lo)*3/4-1 > 8
?? ??? ?// ==> m < b ==> data[m] <= pivot
?? ??? ?if !data.Less(m, pivot) { // data[m] = pivot
?? ??? ??? ?data.Swap(m, b-1)
?? ??? ??? ?b--
?? ??? ??? ?dups++
?? ??? ?}
?? ??? ?// 如果上面的 if 進(jìn)入了兩次, 就證明現(xiàn)在是偏態(tài)分布(也就是左右不平衡的)
?? ??? ?protect = dups > 1
?? ?}
?? ?// 不平衡,接著進(jìn)行處理
?? ?// 這里劃分的是<pivot和=pivot的兩組
?? ?if protect {
?? ??? ?// Protect against a lot of duplicates
?? ??? ?// Add invariant:
?? ??? ?//?? ?data[a <= i < b] unexamined
?? ??? ?//?? ?data[b <= i < c] = pivot
?? ??? ?for {
?? ??? ??? ?// 處理使 data[b] == pivot
?? ??? ??? ?for ; a < b && !data.Less(b-1, pivot); b-- {
?? ??? ??? ?}
?? ??? ??? ?// 處理使 data[a] < pivot
?? ??? ??? ?for ; a < b && data.Less(a, pivot); a++ {
?? ??? ??? ?}
?? ??? ??? ?if a >= b {
?? ??? ??? ??? ?break
?? ??? ??? ?}
?? ??? ??? ?// data[a] == pivot; data[b-1] < pivot
?? ??? ??? ?data.Swap(a, b-1)
?? ??? ??? ?a++
?? ??? ??? ?b--
?? ??? ?}
?? ?}
?? ?// 交換中位數(shù)到中間
?? ?data.Swap(pivot, b-1)
?? ?return b - 1, c
}

對(duì)于這幾種排序算法的使用,sort 包中是混合使用的

1、如果切片長(zhǎng)度大于12的時(shí)候使用快排,使用快排的時(shí)候,如果滿足了使用堆排序的條件沒(méi)這個(gè)排序?qū)τ诤竺娴臄?shù)據(jù)的處理,又會(huì)轉(zhuǎn)換成堆排序;

2、切片長(zhǎng)度小于12了,就使用 shell 排序,shell 排序只處理一輪數(shù)據(jù),后面數(shù)據(jù)的排序使用插入排序;

堆排序和插入排序就是正常的排序處理了

// insertionSort sorts data[a:b] using insertion sort.
// 插入排序
func insertionSort(data Interface, a, b int) {
?? ?for i := a + 1; i < b; i++ {
?? ??? ?for j := i; j > a && data.Less(j, j-1); j-- {
?? ??? ??? ?data.Swap(j, j-1)
?? ??? ?}
?? ?}
}

// 堆排序
func heapSort(data Interface, a, b int) {
?? ?first := a
?? ?lo := 0
?? ?hi := b - a

?? ?// Build heap with greatest element at top.
?? ?for i := (hi - 1) / 2; i >= 0; i-- {
?? ??? ?siftDown(data, i, hi, first)
?? ?}

?? ?// Pop elements, largest first, into end of data.
?? ?for i := hi - 1; i >= 0; i-- {
?? ??? ?data.Swap(first, first+i)
?? ??? ?siftDown(data, lo, i, first)
?? ?}
}

穩(wěn)定排序

sort 包中也提供了穩(wěn)定的排序,通過(guò)調(diào)用sort.Stable來(lái)實(shí)現(xiàn)

// It makes one call to data.Len to determine n, O(n*log(n)) calls to
// data.Less and O(n*log(n)*log(n)) calls to data.Swap.
func Stable(data Interface) {
?? ?stable(data, data.Len())
}

func stable(data Interface, n int) {
?? ?// 定義切片塊的大小
?? ?blockSize := 20 // must be > 0
?? ?a, b := 0, blockSize
?? ?// 如果切片長(zhǎng)度大于塊的大小,分多次對(duì)每個(gè)塊中進(jìn)行排序 ? ?
?? ?for b <= n {
?? ??? ?insertionSort(data, a, b)
?? ??? ?a = b
?? ??? ?b += blockSize
?? ?}
?? ?insertionSort(data, a, n)

?? ?// 如果有多個(gè)塊,對(duì)排好序的塊進(jìn)行合并操作
?? ?for blockSize < n {
?? ??? ?a, b = 0, 2*blockSize
?? ??? ?for b <= n {
?? ??? ??? ?symMerge(data, a, a+blockSize, b)
?? ??? ??? ?a = b
?? ??? ??? ?b += 2 * blockSize
?? ??? ?}
?? ??? ?if m := a + blockSize; m < n {
?? ??? ??? ?symMerge(data, a, m, n)
?? ??? ?}
?? ??? ?// block 每次循環(huán)擴(kuò)大兩倍, 直到比元素的總個(gè)數(shù)大,就結(jié)束
?? ??? ?blockSize *= 2
?? ?}
}

func symMerge(data Interface, a, m, b int) {
?? ?// 如果只有一個(gè)元素避免沒(méi)必要的遞歸,這里直接插入
?? ?// 處理左邊部分
?? ?if m-a == 1 {
?? ??? ?// 使用二分查找查找最低索引 i
?? ??? ?// 這樣 data[i] >= data[a] for m <= i < b.
?? ??? ?// 如果不存在這樣的索引,則使用 i == b 退出搜索循環(huán)。
?? ??? ?i := m
?? ??? ?j := b
?? ??? ?for i < j {
?? ??? ??? ?h := int(uint(i+j) >> 1)
?? ??? ??? ?if data.Less(h, a) {
?? ??? ??? ??? ?i = h + 1
?? ??? ??? ?} else {
?? ??? ??? ??? ?j = h
?? ??? ??? ?}
?? ??? ?}
?? ??? ?// Swap values until data[a] reaches the position before i.
?? ??? ?for k := a; k < i-1; k++ {
?? ??? ??? ?data.Swap(k, k+1)
?? ??? ?}
?? ??? ?return
?? ?}

?? ?// 同上
?? ?// 處理右邊部分
?? ?if b-m == 1 {
?? ??? ?// Use binary search to find the lowest index i
?? ??? ?// such that data[i] > data[m] for a <= i < m.
?? ??? ?// Exit the search loop with i == m in case no such index exists.
?? ??? ?i := a
?? ??? ?j := m
?? ??? ?for i < j {
?? ??? ??? ?h := int(uint(i+j) >> 1)
?? ??? ??? ?if !data.Less(m, h) {
?? ??? ??? ??? ?i = h + 1
?? ??? ??? ?} else {
?? ??? ??? ??? ?j = h
?? ??? ??? ?}
?? ??? ?}
?? ??? ?// Swap values until data[m] reaches the position i.
?? ??? ?for k := m; k > i; k-- {
?? ??? ??? ?data.Swap(k, k-1)
?? ??? ?}
?? ??? ?return
?? ?}

?? ?for start < r {
?? ??? ?c := int(uint(start+r) >> 1)
?? ??? ?if !data.Less(p-c, c) {
?? ??? ??? ?start = c + 1
?? ??? ?} else {
?? ??? ??? ?r = c
?? ??? ?}
?? ?}

?? ?end := n - start
?? ?if start < m && m < end {
?? ??? ?rotate(data, start, m, end)
?? ?}
?? ?// 遞歸的進(jìn)行歸并操作
?? ?if a < start && start < mid {
?? ??? ?symMerge(data, a, start, mid)
?? ?}
?? ?if mid < end && end < b {
?? ??? ?symMerge(data, mid, end, b)
?? ?}
}

對(duì)于穩(wěn)定排序,用到了插入排序和歸并排序

1、首先會(huì)將數(shù)據(jù)按照每20個(gè)一組進(jìn)行分塊,對(duì)每個(gè)塊中的數(shù)據(jù)使用插入排序完成排序;

2、然后下面使用歸并排序,對(duì)排序的數(shù)據(jù)塊進(jìn)行兩兩歸并排序,完成一次排序,擴(kuò)大數(shù)據(jù)塊為之前的2倍,直到完成所有的排序。

查找

sort 中的 查找功能最終是調(diào)用 search 函數(shù)來(lái)實(shí)現(xiàn)的

func SearchInts(a []int, x int) int {
?? ?return Search(len(a), func(i int) bool { return a[i] >= x })
}

// 使用二分查找
func Search(n int, f func(int) bool) int {
?? ?// Define f(-1) == false and f(n) == true.
?? ?// Invariant: f(i-1) == false, f(j) == true.
?? ?i, j := 0, n
?? ?for i < j {
? ? ? ? ? ? ? ? // 二分查找
?? ??? ?h := int(uint(i+j) >> 1) // avoid overflow when computing h
?? ??? ?// i ≤ h < j
?? ??? ?if !f(h) {
?? ??? ??? ?i = h + 1 // preserves f(i-1) == false
?? ??? ?} else {
?? ??? ??? ?j = h // preserves f(j) == true
?? ??? ?}
?? ?}
?? ?// i == j, f(i-1) == false, and f(j) (= f(i)) == true ?=> ?answer is i.
?? ?return i
}

sort 中查找相對(duì)比較簡(jiǎn)單,使用的是二分查找

Interface

sort 包提供了 Interface 的接口,我們可以自定義數(shù)據(jù)結(jié)構(gòu),然后實(shí)現(xiàn) Interface 對(duì)應(yīng)的接口,就能使用 sort 包中的方法

type Interface interface {
?? ?Len() int

?? ?Less(i, j int) bool

?? ?Swap(i, j int)
}

看源碼可以看到 sort 包中已有的對(duì) []int 等數(shù)據(jù)結(jié)構(gòu)的排序,也是實(shí)現(xiàn)了 Interface

// Convenience types for common cases

// IntSlice attaches the methods of Interface to []int, sorting in increasing order.
type IntSlice []int

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

這種思路挺好的,之后可以借鑒下,對(duì)于可變部分提供抽象接口,讓用戶根據(jù)自己的場(chǎng)景有實(shí)現(xiàn)。

對(duì)于基礎(chǔ)的排序,查找只要實(shí)現(xiàn)了 Interface 的方法,就能擁有這些基礎(chǔ)的能力了。

總結(jié)

sort 對(duì)于排序算法的實(shí)現(xiàn),是結(jié)合了多種算法,最終實(shí)現(xiàn)了一個(gè)高性能的排序算法

抽象出了 IntSlice 接口,用戶可以自己去實(shí)現(xiàn)對(duì)應(yīng)的方法,然后就能擁有 sort 中提供的能力了

參考

【文中示例代碼】https://github.com/boilingfrog/Go-POINT/blob/master/golang/sort/sort_test.go
【Golang sort 排序】https://blog.csdn.net/K346K346/article/details/118314382
【John Tukey’s median of medians】https://www.johndcook.com/blog/2009/06/23/tukey-median-ninther/
【code_reading】https://github.com/Junedayday/code_reading/blob/master/sort/sort.go
【go中的sort包】https://boilingfrog.github.io/2022/03/06/go中的sort包/

到此這篇關(guān)于詳解go語(yǔ)言中sort如何排序的文章就介紹到這了,更多相關(guān)go語(yǔ)言 sort排序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Golang中鏈表的創(chuàng)建和讀取

    詳解Golang中鏈表的創(chuàng)建和讀取

    這篇文章主要為大家詳細(xì)介紹了Golang中鏈表的創(chuàng)建和讀取的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起了解下
    2023-12-12
  • Golang時(shí)間及時(shí)間戳的獲取轉(zhuǎn)換超全面詳細(xì)講解

    Golang時(shí)間及時(shí)間戳的獲取轉(zhuǎn)換超全面詳細(xì)講解

    說(shuō)實(shí)話,golang的時(shí)間轉(zhuǎn)化還是很麻煩的,最起碼比php麻煩很多,下面這篇文章主要給大家介紹了關(guān)于golang時(shí)間/時(shí)間戳的獲取與轉(zhuǎn)換的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-12-12
  • Go1.20最新資訊go?arena手動(dòng)管理內(nèi)存鴿了

    Go1.20最新資訊go?arena手動(dòng)管理內(nèi)存鴿了

    由于過(guò)于繁雜,Go?核心團(tuán)隊(duì)成員@Ian?Lance?Taylor,也表態(tài):目前尚未做出任何決定,也不可能在短期內(nèi)做出任何決定,可以認(rèn)為這個(gè)提案基本鴿了,今天這篇文章就是給大家同步目前的情況
    2023-11-11
  • Go語(yǔ)言指針訪問(wèn)結(jié)構(gòu)體的方法

    Go語(yǔ)言指針訪問(wèn)結(jié)構(gòu)體的方法

    這篇文章主要介紹了Go語(yǔ)言指針訪問(wèn)結(jié)構(gòu)體的方法,涉及Go語(yǔ)言指針及結(jié)構(gòu)體的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • Golang中Interface接口的三個(gè)特性

    Golang中Interface接口的三個(gè)特性

    本文詳細(xì)講解了Golang中Interface接口的三個(gè)特性,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • 簡(jiǎn)單高效!Go語(yǔ)言封裝二級(jí)認(rèn)證功能實(shí)現(xiàn)

    簡(jiǎn)單高效!Go語(yǔ)言封裝二級(jí)認(rèn)證功能實(shí)現(xiàn)

    本文將介紹如何使用Go語(yǔ)言封裝二級(jí)認(rèn)證功能,實(shí)現(xiàn)簡(jiǎn)單高效的用戶認(rèn)證流程,二級(jí)認(rèn)證是一種安全措施,要求用戶在登錄后進(jìn)行額外的身份驗(yàn)證,以提高賬戶安全性,
    2023-10-10
  • 深度解密 Go 語(yǔ)言中的 sync.map

    深度解密 Go 語(yǔ)言中的 sync.map

    這篇文章主要介紹了深度解密 Go 語(yǔ)言中的 sync.map,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-06-06
  • 淺談Golang?Slice切片如何擴(kuò)容的實(shí)現(xiàn)

    淺談Golang?Slice切片如何擴(kuò)容的實(shí)現(xiàn)

    本文主要介紹了淺談Golang?Slice切片如何擴(kuò)容的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • Go type關(guān)鍵字(類型定義與類型別名的使用差異)用法實(shí)例探究

    Go type關(guān)鍵字(類型定義與類型別名的使用差異)用法實(shí)例探究

    這篇文章主要為大家介紹了Go type關(guān)鍵字(類型定義與類型別名的使用差異)用法實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • 深入了解Go語(yǔ)言編譯鏈接的過(guò)程

    深入了解Go語(yǔ)言編譯鏈接的過(guò)程

    Go在編譯時(shí)會(huì)將interface和channel關(guān)鍵字轉(zhuǎn)換成runtime中的結(jié)構(gòu)和函數(shù)調(diào)用,所以小編覺(jué)得很有必要就Go的編譯過(guò)程理一理做個(gè)進(jìn)行總結(jié),下面就來(lái)和小編一起了解一下Go語(yǔ)言編譯鏈接的過(guò)程吧
    2023-08-08

最新評(píng)論