目录
[1. 算法原理](#1. 算法原理)
[2. 实现步骤](#2. 实现步骤)
[3. 代码实现](#3. 代码实现)
[4. 时间复杂度分析](#4. 时间复杂度分析)
[6. 优缺点](#6. 优缺点)
[7. 优化方法](#7. 优化方法)
[8. 适用场景](#8. 适用场景)
[9. 与其他排序算法的比较](#9. 与其他排序算法的比较)
[10. 总结](#10. 总结)
1. 算法原理
快速排序(Quick Sort)是一种高效的排序算法,采用分治策略。其核心思想是:
- 选择基准元素:从待排序数组中选择一个元素作为基准
- 分区操作 :将数组分为两部分,使得左边的元素都小于等于基准 ,右边的元素都大于等于基准
- 递归排序:对左右两个子数组分别进行快速排序
2. 实现步骤
- 选择基准元素(通常选择第一个元素、最后一个元素或中间元素)
- 从右向左寻找第一个小于基准的元素
- 从左向右寻找第一个大于基准的元素
- 交换这两个元素
- 重复步骤2-4,直到左右指针相遇
- 将基准元素与相遇位置的元素交换
- 对左右两个子数组递归执行上述步骤
3. 代码实现
cpp
//快速排序的一次划分
int Partition(int* arr, int low, int high)//O(n),O(1)
{
int tmp = arr[low];//基准
while (low < high)
{
//从后往前找比基准小的数字,往前移动
while (low<high && arr[high] > tmp)
{
high--;
}
if (low < high)
{
arr[low] = arr[high];
}
//从前往后找比基准大的数据,往后移动
while (low < high && arr[low] <= tmp)
{
low++;
}
if (low < high)
{
arr[high] = arr[low];
}
}
arr[low] = tmp;
return low;
}
void Quick(int* arr,int low, int high)
{
int par = Partition(arr, low,high);
if (low < par - 1)//左边的数据个数超过一个
{
Quick(arr, low, par - 1);
}
if (par + 1 < high)
{
Quick(arr, par + 1, high);
}
}
//快速排序
void QuickSort(int* arr, int len)//(nlogn),O(logn),不稳定(缺点)
{
Quick(arr, 0, len - 1);
}
4. 时间复杂度分析
- 最佳情况:O(n log n) - 每次分区都将数组均匀分成两部分
- 平均情况:O(n log n) - 大多数情况下的表现
- 最坏情况:O(n²) - 当数组已经有序或逆序时,每次分区只能将数组分成一个元素和其余元素两部分
6. 优缺点
1.优点
- 原地排序,空间复杂度低
- 平均时间复杂度为O(n log n),效率高
- 对于大型数据集表现良好
2.缺点
- 最坏情况下时间复杂度为O(n²)
- 对小规模数据集,插入排序可能更高效
- 不稳定排序(相同元素的相对位置可能改变)
7. 优化方法
- 随机选择基准:避免最坏情况的发生
- 三数取中法:选择第一个、中间和最后一个元素的中位数作为基准
- 插入排序优化:对于小规模子数组(如长度小于10),使用插入排序
- 尾递归优化:减少递归调用栈的深度
- 并行处理:对于大型数组,可以并行处理左右子数组
8. 适用场景
- 大型数据集的排序
- 需要原地排序的场景
- 对平均性能要求较高的场景
9. 与其他排序算法的比较
| 算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 |
|---|---|---|---|---|
| 快速排序 | O(n log n) | O(n²) | O(log n) | 不稳定 |
| 归并排序 | O(n log n) | O(n log n) | O(n) | 稳定 |
| 堆排序 | O(n log n) | O(n log n) | O(1) | 不稳定 |
| 插入排序 | O(n²) | O(n²) | O(1) | 稳定 |
| 冒泡排序 | O(n²) | O(n²) | O(1) | 稳定 |
10. 总结
快速排序是一种高效的排序算法,通过分治策略和原地分区操作,在大多数情况下能够达到O(n log n)的时间复杂度。虽然存在最坏情况的风险,但通过合理的优化措施,可以有效避免这种情况的发生。快速排序是实际应用中最常用的排序算法之一,尤其适用于处理大型数据集。