文章目录
一、从图书馆乱架说开去
最近帮学校整理图书馆时(别问为啥程序猿要干这个),发现管理员大爷的排序方式堪称"行为艺术"——他居然把《C++ Primer》插在《Java编程思想》和《Python从入门到放弃》之间!这种"即兴创作"式的排序方式,让我深刻理解了一个真理:没有算法的世界,注定是混乱的!(说人话:快排真的超重要啊!)
二、快速排序的精髓:分而治之的艺术
快速排序的精妙之处,就像把大象装冰箱分三步:
- 找基准:随便挑本书当分界线(专业术语叫pivot)
- 搞事情:把比它薄的书放左边,厚的放右边(专业术语叫partition)
- 玩递归:对左右两堆书重复以上操作
(偷偷告诉你)这个算法的发明者Tony Hoare当年在莫斯科交流时,居然是在给俄语词典排序时想到这个点子的!所以说,生活中处处是算法啊~
三、手把手实现经典快排(C语言版)
// 关键函数:分区操作
int partition(int arr[], int low, int high) {
int pivot = arr[high]; // 选最后一个元素当基准
int i = (low - 1); // 小于基准的边界指针
for (int j = low; j <= high - 1; j++) {
if (arr[j] < pivot) {
i++;
// 交换操作
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// 把基准放到正确位置
int temp = arr[i+1];
arr[i+1] = arr[high];
arr[high] = temp;
return i + 1;
}
// 递归函数本体
void quickSort(int arr[], int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
// 左右开弓递归处理
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
(代码小贴士)这个版本虽然经典,但有个致命弱点——如果数组本来就是有序的,时间复杂度会退化到O(n²)!别急,后面教你怎么优化。
四、时间复杂度揭秘:从O(n²)到O(n log n)的蜕变
先上结论(快拿小本本记好):
场景 | 时间复杂度 | 出现概率 |
---|---|---|
最佳情况 | O(n log n) | 日常使用 |
最差情况 | O(n²) | 需要规避 |
平均情况 | O(n log n) | 普遍存在 |
(敲黑板)这里要注意啦!快排的性能完全取决于基准的选择质量。就像找对象,选对pivot天天过情人节,选错了…你懂的~
五、三大优化秘籍(面试加分项)
1. 三数取中法
// 在low, high, mid三个位置取中间值
int median_of_three(int arr[], int low, int high) {
int mid = low + (high - low)/2;
// 三数比较的骚操作
if (arr[low] > arr[mid]) swap(&arr[low], &arr[mid]);
if (arr[low] > arr[high]) swap(&arr[low], &arr[high]);
if (arr[mid] > arr[high]) swap(&arr[mid], &arr[high]);
return mid;
}
2. 尾递归优化
把第二个递归调用改为循环,栈深度从O(n)降到O(log n),防止堆栈溢出!
3. 插入排序混合使用
当子数组长度小于10时切换成插入排序,实测速度提升20%+
六、快排的十八般武艺
你以为快排只能排数字?Too young!
- 字符串排序:按字典序排列
- 对象排序:根据指定属性排序
- 多关键字排序:先按姓名再按年龄
- 大数据处理:外排序的基石
(冷知识)Linux系统的sort命令底层就是快排的优化版!下次用sort命令时,可以自豪地说:“这算法我懂!”
七、算法选择困难症的解药
什么时候该用快排?记住这个口诀:
数组较大选快排
数据随机更痛快
内存紧张要慎用
稳定需求请走开
(说人话)数据量大且随机分布时首选快排,需要稳定排序时请选择归并排序。
八、从快排看人生哲学
最后来个程序员式鸡汤:
- 学会取舍:像选择pivot一样抓住关键
- 分治思维:大问题拆成小问题逐个击破
- 递归精神:把复杂交给递归,把简单留给自己
- 持续优化:没有最好的算法,只有更适合的场景
下次面试被问快排,记得微笑说出:“这个算法啊,它教会了我…”(面试官眼睛会发光你信不信?)