++先取中间值,对两边递归,使得两边(假设中间值在左边)顺序满足题目条件,然后用双指针算法合并++
《信奥赛一本通》中提到++归并排序是稳定++ 的,++稳定排序指的是在排序过程中,如果两个元素值相等,排序后它们的先后顺序保持不变++ 。比如原本在前面的相等元素,排完序后依然在前面,不会乱序 。而++快排是不稳定++的。
归并排序每一层都是O(n),一共O(log₂n)层(每次"二分"),乘在一起就是++O(nlog₂n)++
关于快速排序的时间复杂度,DeepSeek讲的很好
**快速排序平均时间复杂度是 O(nlogn),最坏情况是 O(n²)**。计算核心是看递归层数和每层处理的数据量 。百科
复杂度是怎么算出来的
- 每层工作量:每次划分都要把数组从头到尾遍历一遍,不管数据多少,这一步耗时固定是 O(n)。
- 递归层数:理想情况下,每次把数组切成两半,就像折纸一样,切 log₂n 次就只剩 1 个元素了,所以层数是 O(log n)。
- 总时间:把层数乘以每层工作量,就是 O(n) × O(log n) = O(nlogn),这是平均和最好情况 。
- 分治思想:简单来说就是把大问题拆成小问题,小问题排好了,大问题也就好了 。百科
什么时候会变成 O(n²)
- 划分不均匀:如果每次选的基准数都是最大或最小的,数组没法对半切,只能切掉一个元素 。
- 递归变深:这时候递归层数变成了 n 层,总时间就变成了 O(n) × O(n) = O(n²),跟冒泡排序一样慢了 。
- 常见场景:比如数组本来就是有序的,你还固定选第一个数当基准,就会触发最坏情况 。
- 优化建议:随机选基准数或者用"三数取中法",能大概率保持 O(nlogn) 的速度 。
++三数取中法++的意思是x=mid(a1,an+1\>\>1,an) (我自己发明的函数,别当真)
根据上述分析,归并排序按理来说性能大于快排,但++现实显然反的++,我又去问了DeepSeek
在通用内存排序场景中,快速排序的用处更广泛 ,它是大多数编程语言标准库(如 C++
std::sort)的基础或核心组件;但在特定领域(如外部排序、链表排序、稳定性要求高的场景),归并排序具有不可替代的地位。核心结论:为何快速排序应用更广?
- 空间效率优势 :快速排序是原地排序,仅需 O(log n) 栈空间,适合内存受限环境;归并排序需 O(n) 额外空间,在大数组下内存开销显著。
- 缓存友好性:快速排序访问内存连续,CPU 缓存命中率高,实际运行常数因子更小,通常比归并排序快 20%-50%。
- 工程优化成熟:现代实现多采用"三数取中"、"小数组切插排"等优化,极大降低了最坏情况 O(n²) 的发生概率,使其在随机数据下表现极稳。
归并排序的关键不可替代场景
尽管整体使用频率略低,归并排序在以下领域是首选甚至唯一解:
- 需要稳定排序:当相等元素的相对顺序必须保留时(如多关键字排序、金融交易记录),归并排序是稳定算法,而快速排序不稳定。
- **外部排序(大数据)**:当数据量远超内存容量(如磁盘文件排序),归并排序的"分块-合并"机制天然适配,是数据库和大数据框架(MapReduce)的核心算法。
- 链表排序:归并排序在链表上可实现 O(1) 额外空间且效率稳定,而快速排序在链表中分区操作复杂且性能下降。
选型建议
- 默认选快速排序:处理普通数组、对内存敏感、追求极致速度且无需稳定性时。
- 选归并排序:要求排序稳定、数据存储在磁盘/网络(外部排序)、或数据结构为链表时。
注:现代语言标准库常采用混合策略(如 Python 的 Timsort、Java 的对象数组排序),融合了两者优点,但底层逻辑仍偏向归并思想的稳定性保障。
有关"缓存友好性",是因为计算机存数组的内存是连续的,归并排序访问时p1、p2所对应的内存不连续,即"东一个西一个",而快排是首先一顿i++,然后一顿j++,每一次"一顿"都是一小段连续的内存访问
有关外部排序,说实话我没看懂,级别不够QwQ
cpp
void merge_sort(int l,int r){
if (l>=r){
return;
}
int mid=l+r>>1;
merge_sort(l,mid),merge_sort(mid+1,r);
int p1=l,p2=mid+1,p=0;
while (p1<=mid || p2<=r){
if (p1<=mid && (p2>r || a[p1]<=a[p2])){
b[++p]=a[p1++];
}else{
b[++p]=a[p2++];
}
}
_for(i,1,p){
a[l+i-1]=b[i];
}
}
就这样吧