快速排序是二分法排序。首先会选择一个基准元素,然后将基准值和元素内其他元素进行比较。数组一轮遍历后的结果为基准元素以外的元素分为[比基准值小]和[比基准值大]两个类别。整体数组为[比基准值小]基准元素[比基准值大]的结构。
然后再对两个[]中的元素进行排序,重复上述步骤,直到数组排序完成。
文章中使用的动画网站地址,限 pc: 排序算法动画
http://www.donghuasuanfa.com/sort
伪代码来自维基百科
algorithm quicksort(A, lo, hi) is
if lo < hi then
p := partition(A, lo, hi)
quicksort(A, lo, p)
quicksort(A, p + 1, hi)
algorithm partition(A, lo, hi) is
pivot := A[ floor((hi + lo) / 2) ]
i := lo - 1
j := hi + 1
loop forever
do
i := i + 1
while A[i] < pivot
do
j := j - 1
while A[j] > pivot
if i ≥ j then
return j
swap A[i] with A[j]
排序算法动画地址 http://www.donghuasuanfa.com/sort
首先设置最右侧的元素为基准元素,然后坑位定在左侧第一个元素位置。坑位的作用是保证左侧的元素都比基准元素小,右侧的元素都比基准元素大。
顺序遍历数组中每一个元素比较基准元素和各个元素,比较分为俩种情况:
一:如果遍历的元素比基准元素小,则交换坑位所处元素和当前遍历的元素,坑位向右侧移动一个位置。
二:如果遍历元素比基准元素大,则无需交换位置。
最终遍历一次数组后。保证坑位的左侧都比基准元素小,右侧都比基准元素大。
下图1-1示例中。首先设置最右侧29为基准元素。然后和35进行比较,35比基准元素29大,所以比较后只标记颜色即可。
然后基准元素29和第二个元素20进行比较。由于20比29小,所以将坑位所在元素35和20互换位置,然后坑位向右侧移动1的位置。
基准元素29再比较第三个元素21。由于21比29小,所以将21和坑位所在元素35互换位置,然后坑位再向右侧移动1的位置。保证坑位的左侧数组的值都为比基准元素小,右侧数组的值都比基准元素大,坑位的位置基本都为右侧紫色子数组的第一个元素,。
遍历一轮数组后,保证坑位位置的左侧都比基准元素29小,坑位位置的右侧都比基准元素29大。然后将基准元素和坑位元素交换位置。
下图1-2示例中。当前数组最后遍历14的元素,由于14的元素比基准元素小,所以将14的比较元素和坑位所在元素进行位置互换,然后将坑位向右移动。
遍历数组后,将基准元素29和坑位所在元素进行互换位置,则一轮的比较结束。然后再分别对两个子数组中的元素进行排序,重复上述步骤,直到数组排序完成。
快速排序通过遍历元素和基准元素进行比较,找到能够将数组一份为2的位置(坑位)。然后再交换坑位和基准元素的位置,将数组一分为二。最后再递归子数组。直到数组都完成排序。
最差时间复杂度 | 平均时间复杂度 | 最优时间复杂度 |
---|---|---|
O ( N 2 ) O(N^2) O(N2) | O ( N ∗ l o g 2 N ) O(N*log_{2}N) O(N∗log2N) | O ( N ∗ l o g 2 N ) O(N*log_{2}N) O(N∗log2N) |
最差的情况是O(N2),每次分区的结果都是一侧全量数据,一侧无数据。则每次基准元素都要和数组内各个元素进行比较,且比较后无法将数组二等分。然后下一轮基准元素还是和数组内各个元素进行比较。所以时间复杂度为O(N2)。例:1,2,3,4。快速排序将退化为冒泡排序。
如下图1-3所示:
最好的情况的时间复杂度为N * log2N。每次分区都很均匀。即数组能被二分分割。然后子元素再进行比较。
首先基准元素需要和各个元素进行比较,此过程对应的时间复杂度为N。
其次由于分区很均匀。 所以数组能被二分法分割,分割后,可同时对分割后子元素进行处理,对应的时间复杂度log2N。
所以总的时间复杂度为 N * log2N。
快速排序需要一个栈空间来实现递归。最好的情况下,即快速排序的每一趟排序都将元素序列均匀地分割成长度相近的两个子表,所需栈的最大深度为log2(n+1);但最坏的情况下,栈的最大深度为n。这样,快速排序的空间复杂度为O(log2n)
快速排序为不稳定排序,因为当基准元素和另外一个元素数值相同时,基准元素再和各个元素比较后可能变化位置。所以为不稳定排序。 例如 4,5,6,5
如下图1-3所示: