本博客所有内容均整理自《算法图解》,欢迎讨论交流~
快速排序是一种常用的排序算法,比选择排序快很多。C语言标准库中的函数qsort实现的就是快速排序。
快速排序非常实用,它使用了递归和分治法的思想。具体来说,快速排序的思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列的目的。
下面来用数组元素排序的例子详细阐述快速排序。
对排序算法来说,最简单的数组是怎样的呢?很明显,就是根本不需要排序的数组,即空数组和只包含一个元素的数组。
因此,快速排序的基线条件为数组为空或只包含一个元素。在这种情况下,根本就不需要排序,只需要原样返回数组即可。
def quicksort(array):
if len(array) < 2:
return array
那么对于更长的数组呢?
其实思路很简单,对于更长的数组,我们就想办法让它一步步简化,直到也变成最简单的数组即可。这就是快速排序应用分治法思想的方案。具体操作步骤如下:
首先,我们从数组中选择一个元素,这个元素被称为基准值。对于如何选择合适的基准值,我们在此不做介绍,此时我们暂且将数组的第一个元素作为基准值。
然后,我们找出比基准值小的元素以及比基准值大的元素。这个过程被称为分区。于是现在,你有以下三组数据:
- 一个由所有小于基准值的数字组成的子数组;
- 基准值;
- 一个由所有大于基准值的数字组成的子数组。
这里需要注意,我们以上的操作只进行了分区,得到的两个子数组是无序的。当然,如果这两个数组是有序的,对整个数组进行排序就非常容易了,我们只需要执行以下操作即可以得到一个有序的数组:
左边的数组 + 基准值 + 右边的数组 = 有序数组
那么如何对子数组进行排序呢?我们沿用递归的思路,即如果子数组的长度小于2,我们可以直接返回数组作为排好序的序列,而如果数组长度大于2,我们继续选择基准值,并将该子数组划分成更小的子数组。
所以,快速排序的算法步骤如下所示:
- 选择基准值。
- 将数组分成两个子数组:小于基准值的元素和大于基准值的元素。
- 对这两个子数组进行快速排序。
举个实际的例子。
假设你要对以下4个数字进行排序:
33, 10, 15, 7
首先,我们取第一个元素33作为基准值,记作基准值1,于是,我们可以得到以下两个子数组:
小于基准值的元素:10, 15, 7 ——记作数组1
大于基准值的元素:无 ——记作数组2
很明显,小于基准值的元素组成的子数组包含3个元素,我们需要对其进行递归,再次使用快速排序。
于是,我们再次选择子数组的第一个元素10作为基准值,记作基准值2,得到以下两个数组:
小于基准值的元素:7 ——记作数组3
大于基准值的元素:15 ——记作数组4
对于这两个子数组,我们可以直接返回原数组作为已排序序列,所以数组1排序后为:
数组3 + 基准值2 + 数组4 = 数组1(已排序)= 7, 10, 15
获得了已排序的数组1之后,我们再来看数组2,由于数组2是空数组,所以我们也是可以直接返回原数组作为已排序数组。于是我们可以得到已排序的原始数组为:
数组1(已排序)+ 基准值1 + 数组2(已排序) = 原始数组(已排序)= 7, 10, 15, 33
于是就把原来的数组成功完成了排序工作。
下面给出快速排序的Python代码:
def quicksort(array):
if len(array) < 2:
return array
else:
pivot = array[0]
less = [i for i in array[1:] if i < pivot]
greater = [i for i in array[1:] if i > pivot]
return quicksort(less) + [pivot] + quicksort(greater)
细心的朋友可能会说,如果原数组中包含相同的数字,而我们刚好把该数字选做了基准值怎么办呢?其实解决这个问题的方法很简单,我们只需要在划分子数组时,把小于改成小于等于(或把大于改成大于等于)即可。因为我们是在array[1:]中去寻找与array[0]的大小关系的元素,所以array[0]一定会出现在最后[pivot]的位置;而与array[0]相等的数字会被分入子数组,依次参与递归。
所以将以上代码修改一个地方即可,如下所示:
def quicksort(array):
if len(array) < 2:
return array
else:
pivot = array[0]
less = [i for i in array[1:] if i <= pivot]
greater = [i for i in array[1:] if i > pivot]
return quicksort(less) + [pivot] + quicksort(greater)
如此,我们便可以去给任何无序数组进行排序了,哪怕其中有相等的数字。