<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          在Go中如何進行排序

          共 3032字,需瀏覽 7分鐘

           ·

          2020-12-17 02:14

          排序操作在我們?nèi)粘i_發(fā)中是經(jīng)常干的一件事,一些語言會提供開箱即用的工具,比如在 PHP 中關(guān)于數(shù)組對應(yīng)的?sort?函數(shù)。當(dāng)然在 Go 中也有對應(yīng)的實現(xiàn)方式。

          介紹與使用

          Go 中有一個?sort 內(nèi)置包,里面提供了一些排序函數(shù)用來對任何序列進行排序操作。對于基礎(chǔ)的類型,直接提供了對應(yīng)的排序函數(shù),比如下面展示的幾種類型的切片。


          package main
          import ( "fmt" "sort")
          func main() { numbers := []int{18, 8, 28, 38} sort.Ints(numbers) fmt.Printf("int sort:%v\n", numbers) amounts := []float64{10, 1, 8.88, 2, 6} sort.Float64s(amounts) fmt.Printf("float64 sort:%v\n", amounts) strings := []string{"h", "e", "l", "l", "o"} sort.Strings(strings)??fmt.Printf("string?sort:%v\n",?strings)}


          執(zhí)行這段代碼,可以看到各類型的切片已然有序。



          當(dāng)然,不僅僅是切片,我們可以通過?sort.Sort()? 和? sort.Stable()?排序任意數(shù)據(jù)結(jié)構(gòu)。兩者最大的區(qū)別在于?sort.Sort()?不是穩(wěn)定排序,而?sort.Stable()?是穩(wěn)定排序。穩(wěn)定排序意味著對于相同的元素,能保證他們排序后和排序前的順序保持一致。


          什么情況下可以調(diào)用這兩個函數(shù)?


          只要是實現(xiàn)了?sort.Interface?接口的任意類型都可以使用這兩個函數(shù)進行排序操作,至于原因,待會再解釋為什么。我們先來看?sort.Interface?接口。



          主要包含三個方法。獲取結(jié)構(gòu)的長度 Len(),元素比較返回的布爾值?Less()?以及交換兩個元素?Swap(),現(xiàn)在讓我們來實現(xiàn)它。


          package main
          import ( "fmt" "sort")
          type UserInfo struct { Age int Name string}
          type Users []UserInfo
          func (users Users) Len() int { return len(users) }
          func?(users?Users)?Less(i,?j?int)?bool?{???//正序??return?users[i].Age?}
          func (users Users) Swap(i, j int) { users[i], users[j] = users[j], users[i]}
          func main() { userAll := []UserInfo{ {20, "curry"}, {18, "wuqinqiang"}, {27, "張三"}, {10, "李四"}, {25, "王武"},
          } sort.Sort(Users(userAll)) fmt.Println(userAll)}


          我們定義了一個?UserInfo?的結(jié)構(gòu)體,Users?是一個 UserInfo 結(jié)構(gòu)的切片。(注意:我們這里使用的是切片,但是不要把思維限制在切片上,?可以是實現(xiàn)了sort.Interface?接口的任意類型)。這里 Users?實現(xiàn)方法 Less()?是通過對 age?的大小來實現(xiàn)正序排序。根據(jù)元素的比較結(jié)果然后使用 Swap()?來實現(xiàn)元素間的互換。


          users[i],?users[j]?=?users[j],?users[i]


          元素間的互換可以通過以上的操作來進行,這是 Go 的一些特性。當(dāng)然有些其他語言也支持。如果使用的是“世界上最好的語言”的話,那么要實現(xiàn)兩元素之間的互換,多數(shù)人會利用第三方變量,比如:


          $a=1;$b=2;$temp=$a;$a=$b;$b=$temp;

          有那味道了......。



          以上介紹了Go 中 sort?包的一些使用姿勢。現(xiàn)在我們開始稍微深入一點點了解一下底層原理。?


          了解運行原理

          我們先進入 sort.Sort()?方法。

          // Sort sorts data.// It makes one call to data.Len to determine n, and O(n*log(n)) calls to// data.Less and data.Swap. The sort is not guaranteed to be stable.func Sort(data Interface) {  n := data.Len()  quickSort(data, 0, n, maxDepth(n))}

          可以看到,首先會調(diào)用已經(jīng)實現(xiàn)的 Len()?計算長度。然后調(diào)用了一個?quickSort()?(快速排序)的方法,在進入這個方法之前,我們先看看? maxDepth(n)?函數(shù)是啥。

          // maxDepth returns a threshold at which quicksort should switch// to heapsort. It returns 2*ceil(lg(n+1)).func maxDepth(n int) int {  var depth int  for i := n; i > 0; i >>= 1 {    depth++  }  return depth * 2}

          從上面的解釋來看,此函數(shù)返回一個整形閥值,通過這個值來決定是選擇快排還是堆排。

          這段代碼看起來有點蒙,要分成兩段來看:上面主要計算構(gòu)建完全二叉樹的深度,最后 *2 的意思是返回最深的完全二叉樹的深度。大佬的思路果然還是跟不上,emmm......

          再回頭看 quickSort()?函數(shù)

          func quickSort(data Interface, a, b, maxDepth int) {  for b-a > 12 { // Use ShellSort for slices <= 12 elements    if maxDepth == 0 {      heapSort(data, a, b)      return    }    maxDepth--    mlo, mhi := doPivot(data, a, b)    // Avoiding recursion on the larger subproblem guarantees    // a stack depth of at most lg(b-a).    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)    }  }  if b-a > 1 {    // Do ShellSort pass with gap 6    // It could be written in this simplified form cause b-a <= 12    for i := a + 6; i < b; i++ {      if data.Less(i, i-6) {        data.Swap(i, i-6)      }    }    insertionSort(data, a, b)  }}

          單看這段代碼還是好理解的,如果要比較的元素長度大于 12,那么就在堆排序和快速排序之間做選擇。否則的話,就使用希爾排序和插入排序,可以看到希爾排序的間隔為 6。

          這個快速排序方法中調(diào)用了?Swap()?和?Less()?方法,現(xiàn)在,再回頭看之前的 Less(),比較的規(guī)則是由你來決定的, sort.Sort()?會根據(jù)你定義的比較規(guī)則加上當(dāng)前的數(shù)量級來選擇相對應(yīng)的算法進行規(guī)則排序。

          這里還有一段代碼

          func quickSort(data Interface, a, b, maxDepth int) {  // .........................................................
          ????mlo,?mhi?:=?doPivot(data,?a,?b)????// Avoiding recursion on the larger subproblem guarantees // a stack depth of at most lg(b-a). 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) }??????//?.................... }

          實現(xiàn)快排的方式是遞歸,在獲取分區(qū)點的時候返回的是兩個值(多返回值是 Go 的特性)。但是代碼中,在返回分區(qū)點之后,并沒有進行左右分區(qū)的遞歸查找,而是根據(jù)比較的結(jié)果,把數(shù)量多的一方放到下次循環(huán)中繼續(xù)切割,小的直接遞歸。

          因為實現(xiàn)的是遞歸,需要考慮到棧溢出的問題,這里也標(biāo)明調(diào)用?quickSort()?的最高棧深度為?log(b-a),即 log(n)

          剩下的關(guān)于獲取分區(qū)點?doPivot()?方法的實現(xiàn),感興趣的可以自己手手看。

          再看搜索

          既然已經(jīng)實現(xiàn)了排序,那么排序后咋么獲取指定元素所處的位置?? sort?包還提供了?Search?方法。下面是實現(xiàn):

          func main() {  userAll := []UserInfo{    {20, "curry"},    {18, "wuqinqiang"},    {18, "測試2"},    {27, "張三"},    {10, "李四"},    {25, "王武"},  }  sort.Sort(Users(userAll))  temp:=18  //搜索  index := sort.Search(len(userAll), func(i int) bool {    return userAll[i].Age >= temp  })??//判斷元素是否存在  if index < len(userAll) && userAll[index].Age == temp {    fmt.Printf("元素:%d處于索引:%d處\n",temp,index)  }else{    fmt.Printf("元素:%d不存在\n",temp)  }}

          我們可以進入 sort.Search()?查看,好吧,本質(zhì)上就是一個二分查找。

          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 {??//獲取中位數(shù)    h := int(uint(i+j) >> 1) // avoid overflow when computing h    // i ≤ h < j????//這里的f?就是執(zhí)行我們傳進來的????//func(i int) bool {    // return userAll[i].Age >= temp?//? }    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}

          照著這里的邏輯,我們之前?age=18?,返回的是索引 1,假設(shè)我想返回的是相同值的最后一個索引處(對標(biāo) LeetCode34),那么可以自己實現(xiàn)一個自定義的二分查找:

          userAll := []UserInfo{    {20, "curry"},    {18, "wuqinqiang"},    {18, "測試2"},    {18, "測試3"},    {18, "測試4"},    {18, "測試5"},    {18, "測試6"},    {27, "張三"},    {10, "李四"},    {25, "王武"},  }  sort.Sort(Users(userAll))  temp:=18  index := SearchLast(len(userAll)-1, func(i int) bool {    return userAll[i].Age <= temp  })  if index < len(userAll) && userAll[index].Age == temp {    fmt.Printf("元素:%d處于索引:%d處\n",temp,index)  }else{    fmt.Printf("元素:%d不存在\n",temp)  }}
          func SearchLast(n int, f func(int) bool) int { i, j := 0, n last := -1 for i <= j { h := int(uint(i+j) >> 1) // avoid overflow when computing h if f(h) { last, i = h, h+1 } else { j = h - 1 } } return last}


          這篇文章到這里也就結(jié)束了,介紹了 sort?包一小部分的東西,還有很多東西我并未提到,一個是自己也還沒去用,另一個是有些東西,自己去發(fā)現(xiàn)更有樂趣。共勉。



          推薦閱讀


          福利

          我為大家整理了一份從入門到進階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進階看什么。關(guān)注公眾號 「polarisxu」,回復(fù)?ebook?獲??;還可以回復(fù)「進群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

          瀏覽 48
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  www.欧美三级片 | 黄片高清无码免费看 | 亚洲国产欧美久久 | 99精品观看| 国产精品 久久久精品岩 |