快排重要性不用说,直接背诵代码,每天敲一遍,但是要知道快排的原理和复杂度分析,这两个比代码本身更重要。
1.代码:
vector<int> Sort::QuickSort(vector<int> &num, int left, int right)
{
if(left < right)
{
int p = Partition(num, left, right);
QuickSort(num, left, p - 1);
QuickSort(num, p + 1, right);
}
return num;
}
int Sort::Partition(vector<int> &num, int start, int end)
{
int pivot = num[start];//中轴元素
int left = start, right = end;
while(left < right)
{
while(left < right && num[right] > pivot)
{
--right;
}
num[left] = num[right];//交换 num[left]的值已经备份在了pivot
while(left < right && num[left] <= pivot)
{
++left;
}
num[right] = num[left];
}
num[left] = pivot;//中轴放到left位置处
return left;//返回中轴位置
}
2.原理
转自C++经典排序算法总结
假设我们现在对“6 1 2 7 9 3 4 5 10 8”这个10个数进行排序。首先在这个序列中随便找一个数作为基准数(不要被这个名词吓到了,就是一个用来参照的数,待会你就知道它用来做啥的了)。为了方便,就让第一个数6作为基准数吧。接下来,需要将这个序列中所有比基准数大的数放在6的右边,比基准数小的数放在6的左边,类似下面这种排列:
3 1 2 5 4 6 9 7 10 8
在初始状态下,数字6在序列的第1位。我们的目标是将6挪到序列中间的某个位置,假设这个位置是k。现在就需要寻找这个k,并且以第k位为分界点,左边的数都小于等于6,右边的数都大于等于6,递归对左右两个区间进行同样排序即可。想一想,你有办法可以做到这点吗?这就是快速排序所解决的问题。
快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。它的平均时间复杂度为O(nlogn),最坏时间复杂度为O(n^2).
首先上图:
从图中我们可以看到:
left指针,right指针,base参照数。
其实思想是蛮简单的,就是通过第一遍的遍历(让left和right指针重合)来找到数组的切割点。
第一步:首先我们从数组的left位置取出该数(20)作为基准(base)参照物。(如果是选取随机的,则找到随机的哨兵之后,将它与第一个元素交换,开始普通的快排)
第二步:从数组的right位置向前找,一直找到比(base)小的数,如果找到,将此数赋给left位置(也就是将10赋给20),此时数组为:10,40,50,10,60, left和right指针分别为前后的10。
第三步:从数组的left位置向后找,一直找到比(base)大的数,如果找到,将此数赋给right的位置(也就是40赋给10),此时数组为:10,40,50,40,60, left和right指针分别为前后的40。
第四步:重复“第二,第三“步骤,直到left和right指针重合,最后将(base)放到40的位置, 此时数组值为: 10,20,50,40,60,至此完成一次排序。
第五步:此时20已经潜入到数组的内部,20的左侧一组数都比20小,20的右侧作为一组数都比20大, 以20为切入点对左右两边数按照"第一,第二,第三,第四"步骤进行,最终快排大功告成。
3.复杂度分析
时间:平均O(nlogn) 最坏O(n2) 最好O(nlogn)
空间:O(nlogn) 递归开启的栈空间
3.1简单易懂版:
数组有n个元素,要递归运算算出枢纽pivot的位置,然后递归调用左半部分和右边半部分,可以看下面的对应表:
层 | 这层有的元素 | 元素数量 |
---|---|---|
1 | n/2, n/2 | 2 |
2 | n/4, n/4, n/4, n/4 | 4 |
3 | n/8, n/8, n/8, n/8, n/8, n/8, n/8, n/8 | 8 |
… | … | … |
log n | … | n |
第1层分开之后,有n/2,n/2,有2个元素;
第2层分开之后,有4个元素;
以此类推。
那当元素数为n时,会有多少层呢?
设层数为x,那么2^x = n,所以x = logn,也就是元素数为n时有logn层。
然后每层是n的复杂度,所以最终复杂度为nlogn
3.2一个严格的证明:
=均来自知乎=
如何证明快速排序法的平均复杂度为 O(nlogn)?