1. 程式人生 > 實用技巧 >快速排序演算法 - go實現

快速排序演算法 - go實現

在分析redis叢集中大Key的時候,通常都採用分析rdb檔案的方式;但是這種方式需要在每一臺redis伺服器上部署分析程式及分析指令碼,而像salt之類的工具運維沒有開放給我們使用,一臺一臺部署不好管理。正好我們的總redis規模不大,大概在200個叢集左右,考慮到cluster叢集,需要分析的redis例項數在300左右,所以就想著能不能通過scan的方式來進行。

通過scan命令掃描從庫,將叢集中key深度大於指定值的key掃描出來,將這些 key 放在一個slice中,取 top N,這就需要對 slice中的 key按照深度進行排序。300個例項說多不多,說少不少,排序演算法的效能還是很重要的。快速排序針對小資料量排序效能很好,正好mysql在使用sort buffer進行排序時採用的是快速排序,這裡就用go實現來複習一下快速排序演算法。

func sortBigKeySlice(bigKeySlice []*bigKeyInstance) {
   if len(bigKeySlice) < 2 {
      return
   }
   sortBigKey(bigKeySlice, 0, len(bigKeySlice))
}

//使用遞迴實現
func sortBigKey(bigKeySlice []*bigKeyInstance, low, high int) {
   if low >= high {
      return
   }
   p := partition(bigKeySlice, low, high)
   sortBigKey(bigKeySlice, low, p)
   sortBigKey(bigKeySlice, p + 1, high)
}
//普通快速排序,對於普通快速排序,將第一個元素作為基準,小於該元素的放在左邊,大於等於該元素的放在右邊
func partition(bigKeySlice []*bigKeyInstance, low, high int) int{
   //直接將第一個元素作為分隔值
   pivotIns := bigKeySlice[low]
   //當前第一個元素點位作為標記點
   pivotPos := low

   //去除第一個分割值,遍歷元素,如果元素比分割值小,將標記點右移一位,交換元素的值,大於等於則繼續比較下一個元素
   for i := 1; i < high; i++ {
      if bigKeySlice[i].size < pivotIns.size {
         pivotPos += 1
         bigKeySlice[pivotPos], bigKeySlice[i] = bigKeySlice[i], bigKeySlice[pivotPos]
      } else {
         continue
      }
   }
   //不要忘記最後的互換,將分隔值與標記點元素互換
   bigKeySlice[low], bigKeySlice[pivotPos] = bigKeySlice[pivotPos], bigKeySlice[low]
   return pivotPos
}

  

普通快速排序預設左邊的第一個元素作為基準數,對於漸進有序的陣列來說,這就導致小於基準的數會相當少,而大於等於基準的數相當多,造成分割槽不平衡的問題,普通排序就會退化,嚴重的將退化成O(n^2)。所以對其改進:不再預設選擇第一個數,而是隨機選一個數作為基準,這樣的快排稱為隨機普通快排。

//隨機普通快速排序,不使用第一個元素作為基準,而是使用一個隨機元素作為基準
func partition(bigKeySlice []*bigKeyInstance, low, high int) int { //取slice中的一個隨機元素作為分割點,而不是第一個元素開始分割 rand_low := low + rand.Intn(high - low) bigKeySlice[low], bigKeySlice[rand_low] = bigKeySlice[rand_low], bigKeySlice[low] pivotPos := low for i := low + 1; i < high; i++ { if bigKeySlice[i].size < bigKeySlice[low].size { pivotPos += 1 bigKeySlice[pivotPos], bigKeySlice[i] = bigKeySlice[i], bigKeySlice[pivotPos] } else { continue } } bigKeySlice[low], bigKeySlice[pivotPos] = bigKeySlice[pivotPos], bigKeySlice[low] return 0 }

  

對於含有大量重複元素的陣列,則對於與基準數相同的數,要麼分到了左邊,要麼分到了右邊,同樣會造成分治不平衡的問題,造成效能退化。這時,採用雙路排序或三路排序進行改進。

雙路排序 & 三路排序待續....