快速排序
该算法是采用分治法的一个非常典型的应用。
适用场景
- 快速排序在大多数情况下都是适用的,尤其在数据量大的时候性能优越性更加明显。
- 快速排序是基于分治算法递归实现的,每次递归会导致进栈出栈而造成缓慢,所以小规模数据并不适合使用快速排序
算法描述
选择基准
- 从数列中挑出一个元素,称为"基准"(pivot),按照基准对待排序列进行分区(partition)操作
分治法-分:分区(partition)操作
- 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。
分治法-治:递归
递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。
算法复杂度
最好情况
最坏情况
T(N) = O(N²)
算法描述
选择基准
1.选取第一个元素为基准
- 当选取第一个元素为基准后,会导致以后每次递归都只消去一个元素,所以不能选取首尾元素作为基准
2.使用随机函数(rand函数或者自定义伪随机函数)
- 随机函数也会造成一定的开销
- 使用取头中尾的中位数(如 8、12、3中位数就是8)
- 最后将基准放置到右面的原因是为了在划分子集(小于基准的放置在左边,大于基准的放置在右边)时方便的做出最后一步交互(将奇数个元素相互交互后导致的最后一个元素无法确定所在位置)
子集划分
- 设置两个指针i(指向子集起始位置)、j(指向子集结束位置前一个元素(因为最后的元素放置的是基准))
- 比较起始指针i所指元素是否小于基准,若不小于发出一个警告,否则起始指针i指向下一个元素
- 比较结尾指针j所指元素是否大于基准,若不大于发出一个警告,否则起始指针j指向前一个元素
- 当i、j所指向的元素都发出警告后交互两个元素
- 重复执行上述步骤,直到起始指针i越过了末尾指针j(i-j<0),将基准与i所指向元素交互(大基准的元素)
子集中有元素等于基准
交换
- 当子集划分中有元素等于基准时进行交换,虽然进行了许多无谓的交换但是最后基准元素会被换到中间,使得基准在子集的中间位置,此时再进行递归时左右两个序列近似等长为N/2
- 时间复杂度为NlogN
不交换
- 当子集划分中有元素等于基准时不进行交换,然后继续执行,此时有可能导致基准元素没有被交换到中间位置,出现每次递归时基准出现再端点
- 时间复杂度为N²
小规模数据处理
- 快速排序是基于分治算法递归实现的,每次递归会导致进栈出栈而造成缓慢,所以小规模数据并不适合使用快速排序
- 解决方案
- 当递归的数据充分小的时候,停止递归,直接调用简单排序