快速排序演算法 - go實現
阿新 • • 發佈:2020-08-04
在分析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 }
對於含有大量重複元素的陣列,則對於與基準數相同的數,要麼分到了左邊,要麼分到了右邊,同樣會造成分治不平衡的問題,造成效能退化。這時,採用雙路排序或三路排序進行改進。
雙路排序 & 三路排序待續....