数据结构快速排序白话讲解,学会快速排序


该算法是采用分治法的一个非常典型的应用。

适用场景

  • 快速排序在大多数情况下都是适用的,尤其在数据量大的时候性能优越性更加明显。
  • 快速排序是基于分治算法递归实现的,每次递归会导致进栈出栈而造成缓慢,所以小规模数据并不适合使用快速排序

算法描述

在这里插入图片描述

选择基准

在这里插入图片描述

  • 从数列中挑出一个元素,称为"基准"(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²

小规模数据处理

  • 快速排序是基于分治算法递归实现的,每次递归会导致进栈出栈而造成缓慢,所以小规模数据并不适合使用快速排序
  • 解决方案
    • 当递归的数据充分小的时候,停止递归,直接调用简单排序

完整代码

递归控制

在这里插入图片描述

选择基准

在这里插入图片描述