快速排序中的算法思想
1. 分治思想
分治法的基本思想是:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。
我们可以利用分治思想将杂乱无序的数组Arr[p,,r]分为以下几个步骤
1.分解:
在数组Arr[p,,r]中找出主元x,并且依据这个x对原始的数组进行分解成两个数组Arr[p,,,q - 1]和Arr[q + 1,,r]是的任何一个属于Arr[p,,,q - 1]的元素都要小于Arr[q]
任何一个属于Arr[q + 1,,r]的元素都要大于Arr[q]
2.进行递归求解:
通过递归调用快速排序对子数组Arr[p,,,q - 1]和Arr[q + 1,,r]进行排序
3.组合
因为快速排序是基于原址排序的,所以不需要进行合并操作,也就是说此时的数组已经排好序了
代码实现:
伪代码:
quickSort(A, p, r) if p < r q = partition(A, p, r) quickSort(A, p, q - 1) quickSort(A, q + 1, r)
在这里呢,为了排序一个数组的全部元素,初始调用的是A(A, 0, A.length)
其实在数组的分解中最终要的就是他的partition部分,这也是快速排序的关键所在
Go语言的实现:
package main import "fmt" func partition(A []int, p int, r int) int { x := A[r-1] i := p - 1 for j := p; j < r-1; j++ { if A[j] <= x { i += 1 A[i], A[j] = A[j], A[i] } } A[i+1], A[r-1] = A[r-1], A[i+1] return i + 1 } func quickSort(A []int, p int, r int) { if p < r { q := partition(A, p, r) fmt.Println(q) quickSort(A, p, q-1) quickSort(A, q+1, r) } } func main() { arr := []int{2, 8, 7, 1, 3, 5, 6, 4} quickSort(arr, 0, len(arr)) for _, v := range arr { fmt.Println(v) } }
快速排序的性能
在快速排序中,它的运行时间取决于他是否是划分平衡的,如果划分的平衡那么他的时间复杂度和归并排序一样是nlog2(n),当划分不平衡时,时间复杂度就和插入排序一样是O(n^2),但是当元素的个数大于20,快速排序的性能远远好于归并和堆排序(在划分平衡的情况下),这里也就是说他的平均性能是十分的优越的
1.最坏的情况:
当数组A[p,,r]在划分时产生了这么两个数组,他们的元素个数分别是A.length - 1和0,可想而知,当算法每一次递归调用时,都出现了这样极为不平衡的情况,那么划分的时间复杂度就是Θ(n),由于对于一个数组元素个数为0的数组的递归调用,就会直接返回,因此T(0) = Θ(1)
最终算法运行时间为:T(n) = T(n - 1) + Θ(n)
2.最好情况的划分:
其实在快速排序中一般或者绝大多数排序都是更加的接近于最好的情况,而不是最糟糕的情况,
在快速排序中,只要你划分的比例是常数比例,算法的时间复杂度总是O(nlogn),也就是说,哪怕你划分的结果是9999:1,时间复杂度还是O(nlogn);
说明:
本篇属于原创,允许转载,但是请标明原始的连接!!
参考:《算法导论》
原文:http://www.cnblogs.com/struCoder/p/3913738.html