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随机选取时的递归栈高度: