概要:本期我们学习排序算法中的快速排序,会着重讲解算法的核心思想、时空复杂度分析以及代码的实现。
一、快速排序
快速排序(Quicksort)是一种常见的排序算法。
二、核心思想
快速排序的核心思想是分治法,即将问题分解成小的子问题来解决。具体来说:
- 快速排序的基本思想是选取一个枢轴(pivot),作为基准元素。
- 然后将数组分为两部分,使得左边的元素都小于等于枢轴,右边的元素都大于等于枢轴。
- 接着对左右两部分分别递归地进行快速排序,直到排序完成。
跳转到这个网页,直观地感受归并排序吧:)
三、时空复杂度分析
快速排序的时间复杂度分析可以从最好、最坏和平均三个层面进行分析。
-
最好情况是当每次选取的枢轴都刚好是当前子序列的中位数时,此时每次划分都能够把序列均分为两个长度相等的子序列。在这种情况下,快速排序的时间复杂度为O(nlogn)。
-
最坏情况是当每次选取的枢轴都是当前子序列的最小或最大值,此时每次划分都只能将序列划分成一个子序列和一个空序列,这种情况下快速排序的时间复杂度为O(n^2)。
-
在平均情况下,快速排序的时间复杂度为O(nlogn)。快速排序的平均时间复杂度的分析比较复杂,需要用到数学期望的知识。假设每次选取的枢轴都能将当前序列分为长度相等的两个子序列,那么快速排序的时间复杂度就是O(nlogn)。在实际情况下,每次选取的枢轴并不能完美地将序列均分,但是经过证明,平均情况下每次划分会将序列分为比例为3:7左右的两个子序列,因此时间复杂度为O(nlogn)。
在快速排序的过程中,我们使用了递归的方式对序列进行划分和排序,每次递归都会占用一定的栈空间。
- 在最坏情况下,快速排序的递归深度为n,此时需要O(n)的栈空间。
- 在平均情况下,快速排序的递归深度为logn,因此需要O(logn)的栈空间。
因此,快速排序的空间复杂度是O(logn)。需要注意的是,由于快速排序是一种原地排序算法,不需要额外的辅助空间进行排序,因此除了栈空间外,快速排序并不需要额外的空间。
然后,由于快速排序过程中需要通过递归方式来进行序列划分,其中会导致元素相对顺序改变,因此,快速排序不是一种稳定排序算法。
四、代码实现
下面展示C++的实现源码:
void SortFuncation::TraverQuickSort(QVector<int> &arr,int left,int right)
{
// 定义左右指针、中轴值和临时变量
int i = left, j = right;
int pivot = arr[(left + right) / 2];
int tmp;
// 分区操作
while (i <= j)
{
while (arr[i] < pivot)
i++;
while (arr[j] > pivot)
j--;
if (i <= j) {
// 交换 arr[i] 和 arr[j] 的值
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
}
// 递归调用
if (left < j)
TraverQuickSort(arr, left, j);
if (i < right)
TraverQuickSort(arr, i, right);
}
结尾
对于平均时间复杂度为O(n log n)级别的排序算法的学习到这就结束了,我们来简单做一些总结:
算法名称 | 是否为原地排序 | 是否为稳定排序 | 最好的时间复杂度 | 最坏的时间复杂度 | 平均的时间复杂度 |
---|---|---|---|---|---|
归并排序 | 否 | 是 | O(n log n) | O(n log n) | O(n log n) |
快速排序 | 是 | 否 | O(n log n) | O(n^2) | O(n log n) |
下期我们开始学习一些外部排序算法:)