因高中学业繁忙,很久没在论坛发帖了
看到论坛有选择排序的讲解,我也来个快速排序的讲解
快速排序可以分为两个部分,分区和递归。快速排序对一个数组进行排序,实际上是先选取一个基准数(pivot),把比基准数小的数字放到数组左边,比基准数大的数字放到数组右边,最后再分别对拆分出来的左右俩区间递归,分别重复上述过程。
快速排序一般有两种分区方法,Lomuto和Hoare,这里先讲比较易懂的Lomuto分区方法。话不多说,先上代码!
[C++] 纯文本查看 复制代码 int partition(std::vector<int>& arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; ++j) {
if (arr[j] <= pivot) {
++i;
std::swap(arr[i], arr[j]);
}
}
std::swap(arr[i + 1], arr[high]);
return i + 1;
}
void quickSort(std::vector<int>& arr, int low, int high) {
if (low < high) {
int pivotIndex = partition(arr, low, high);
quickSort(arr, low, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, high);
}
}
这里的partition函数分区的过程如下:
首先,取最后一个数为基准数。接着循环遍历排序区间(不包括最后面的基准数)的每一个元素,一旦发现有元素小于等于基准数,就把元素移动到第一个位置,如果再发现一个,就移动到第二个位置,然后是第三个、第四个……以此类推。(注意这里并不是先删除再插入,而是直接交换。因为删除和插入的时间复杂度是O(n))。最后,把最后面的基准数移动到左区间(比基准数小的区间)后面,就完成了分区工作。
当然我们也可以随机选取基准数,只需要在分区开始时把基准数移动到最后一个位置即可。
quickSort函数首先会调用partition进行分区,并从返回值中获取基准数的位置,然后继续对基准数左边的区间和右边的区间进行同样的操作(分区、递归),这些步骤完成后,排序就成功了。
为方便大家理解,我再举一个例子来说明排序的过程,假设原数组序列如下:
7639 9899 9056 8942 6462 3285 9987 3910 9283 8858
我们选取最后的8858作为基准数,发现7637比基准数8858小,于是和第一个数字交换位置,同时计数器i自增1。接着判断第二个位置,9899比8858大,不进行交换,第三个、第四个位置同理。第五个位置是6462,比基准数8858小,于是和第二个数字交换位置。第六个位置是3285,同样比8858小,于是和第三个数字交换位置……
分区的结果如下:
7637 6462 3285 3910 8858 9056 9987 8942 9283 9899
左边的区间(7637 6462 3285 3910)比基准数8858小,右边的区间(9056 9987 8942 9283 9899)比基准数8858大,对两个区间进行同样的操作,最终就得到了排序后的序列:
3285 3910 6462 7637 8858 8942 9056 9283 9899 9987
这就是Lomuto分区的过程了。Lomuto分区方法比Hoare分区方法更容易理解一些,但是对于同样的序列,其比较次数一般比Hoare分区多,排序时间一般比Hoare分区长。 |