归并排序
归并排序将一个无序的N长度数组切成N个有序的子序列,然后两两合并,最后推得一个完整的有序数组。
基本流程:分解,求解(排序),合并
合并:开辟一个临时空间,比较前后两段数组
void sortMerge(vector<int> &input, int left, int mid, int right, vector<int> &tmp){
int i = left;
int j = mid + 1;
int index = 0;
while(i <= mid && j <= right){
if(input[i] <= input[j]){
tmp[index++] = input[i++];
}else{
tmp[index++] = input[j++];
}
}
//如果前一段还有元素,就全赋值到tmp数组
while(i <= mid){
tmp[index++] = input[i++];
}
//如果后一段还有元素,就全赋值到tmp数组
while(j <= right){
tmp[index++] = input[j++];
}
index = 0;
while(left <= right){
input[left++] = tmp[index++];
}
}
合并之前需要数组的分解,用递归实现
void split(vector<int> &input,int left, int right,vector<int> &tmp){
if(left == right) return;
int mid = (left + right)/2;
split(input, left, mid, tmp);
split(input, mid + 1, right, tmp);
//排序并合并
sortMerge(input, left, mid, right, tmp);
}
归并的顺序:先将初始数组分为两部分,先归并低段位,再归并高段位。对低位与高位继续分解,低位分解更低,高位分解更高…
归并排序的栈帧:
主函数调用split(arr,0,4,tmp)函数后:
left参数值为0,right参数值为4,不满足退出条件,然后调用split(arr,0,2,tmp),split(arr,0,2,tmp)被压入栈中,此时栈的状态如下:
split(arr,0,2,tmp)不能立即返回,它在内部又会调用split(arr,0,1,tmp),split(arr,0,1,tmp)又调用了split(arr,0,0,tmp),此时栈中的状态:
程序运行到这里,终于有一个方法可以返回了结果了——split(arr,0,0,tmp),该函数的执行逻辑是对数组中的下标从0到0的元素进行归并,该段只有一个元素,所以不用归并,立即return
split(arr,0,0,tmp)从栈中弹出,这时候程序到代码段(三)中的第二行:split(arr,1,1,tmp),入栈,不用归并,直接返回
程序到代码段(三)的第三行,sortMerge(arr,0,0,1,tmp),对数组中的前两个进行排序
程序跳到代码段(二)的第二行,split(arr,2,2,tmp),不用归并,直接返回
程序到代码段(二)的第三行,sortMerge(arr,0,1,2,tmp),对数组中的前三个进行排序
程序到代码段(四)的第二行,split(arr,3,4,tmp),入栈,归并,排序,返回
程序到代码段(四)的第三行,sortMerge(arr,0,2,4,tmp),对数组中的左半部分与右半部分排序
链表的归并排序
(1)递归实现归并排序(空间复杂度log(n)
)
(2)迭代实现归并排序
一、通过快慢指针找到链表中点
二、递归返回的条件:只有一个节点时返回. 注意这里不是和“NULL”比较,应该和“end”比较
... sortMerge(...){
1.满足递归退出条件时,返回
2.找到链表中点
3.返回:merge(前半段链表,后半段链表)
}
三、合并两个有序链表[两种思路]