Golang实现快速排序 && pivot选取的性能比较
递归完成所有元素的快速排序,根据基准值pivot的选取方式不同,有两种实现方案:
(1).pivot每次选取第一个元素,这样递归栈的长度是n:
func qSort(nums []int) {
length := len(nums)
if length <= 1 {
return
}
low, high := 0, length-1
pivot := low
for low < high {
for low < high && nums[high] > nums[pivot] {
high--
}
for low < high && nums[low] < nums[pivot] {
low++
}
nums[low], nums[high] = nums[high], nums[low]
high--
}
qSort(nums[:low])
qSort(nums[low+1:])
}
(2).pivot从中间选取,这样递归栈的长度小于n:
func getPivot(nums []int, low, high int) int {
pivot := nums[low]
for low < high {
for low < high && nums[high] >= pivot {
high--
}
nums[low] = nums[high]
for low < high && nums[low] <= pivot {
low++
}
nums[high] = nums[low]
}
nums[low] = pivot
return low
}
func qSort(nums []int) {
var backTrack func(num []int, low, high int)
backTrack = func(num []int, low, high int) {
if high > low {
pivot := getPivot(num, low, high)
backTrack(num, low, pivot)
backTrack(num, pivot+1, high)
}
}
backTrack(nums, 0, len(nums)-1)
}
对长度为10的int切片,排序一百万次,两种快速排序进行性能比较:
func main() {
x := []int{4, 2, 8, 6, 0, 5, 1, 7, 3, 9}
tmp := make([]int, 10)
// 第一种方法
start := time.Now()
for i := 0; i < 1000000; i++ {
copy(tmp, x)
qSort1(tmp)
}
cost := time.Since(start)
fmt.Printf("方法一用时:[%v]\n", cost)
// 第二种方法
start = time.Now()
for i := 0; i < 1000000; i++ {
copy(tmp, x)
qSort(tmp)
}
cost = time.Since(start)
fmt.Printf("方法二用时:[%v]\n", cost)
}
输出几组结果如下:
经过比较,pivot从中间选取,是要比pivot每次取第一个的性能要好的,优化程度大概在8%~15%之间,
如何理解这个原因呢?是因为pivot每次选第一个时,递归栈的高度是n,见《算法图解》中的下图:
pivot每次选第一个的递归栈高度:
pivot随机选取时的递归栈高度: