归并排序(Merge Sort)
核心:分治思想+递归策略
将待排序序列不断划分为左、右两部分,然后将左、右部分分别排序后再进行合并,最后逐级合并得到有序序列。
基本思路(升序排序)
- 将待排序序列尽可能均分为左、右两个部分
- 将左部分继续划分,直到只有一个元素为止
- 将右部分继续划分,直到只有一个元素为止
- 归并各级的左、右两部分(分别取左、右部分的元素按照升序合并为一个新序列)
- 逐级归并直到所有的左、右部分合并完成
过程模拟
代码参考
//合并函数
void merge(int arr[],int tmp[],int left,int mid,int right) {
//标记左半区第一个未排序的元素
int l_pos=left;
//标记右半区第一个未排序的元素
int r_pos=mid+1;
//临时数组元素下标
int pos=left;
//合并
while(l_pos <= mid && r_pos <= right) {
//取左、右半区较小的元素
if(arr[l_pos] < arr[r_pos]) {
tmp[pos++]=arr[l_pos++];
} else {
tmp[pos++]=arr[r_pos++];
}
}
//合并左半区剩余的元素
while(l_pos <= mid) {
tmp[pos++]=arr[l_pos++];
}
//合并右半区剩余的元素
while(r_pos <= right) {
tmp[pos++]=arr[r_pos++];
}
//临时数组中合并后的元素复制为原数组
while(left <= right) {
arr[left] = tmp[left];
left++;
}
}
//递归划分函数
void mSort(int arr[],int tmp[],int left,int right) {
//只有一个元素,序列有序,不需要继续划分
if(left==right) return ;
//多个元素
int mid=(left+right)/2; //找中间点
mSort(arr,tmp,left,mid); //递归划分左半区
mSort(arr,tmp,mid+1,right); //递归划分右半区
merge(arr,tmp,left,mid,right); //合并
}
//归并排序入口函数
void MergeSort(int arr[],int n) { //数组a,数组a的长度n
//辅助数组,分配存储空间
int *tmp=(int*)malloc(n*sizeof(int));
if(tmp) { //分配内存成功
mSort(arr,tmp,0,n-1);//递归划分
free(tmp);//释放内存
} else { //分配内存失败
cout <<"error";
}
}
复杂度
时间复杂度:O(n*log n),对与规模为n的问题,每一层合并的复杂度为O(n),一共进行log n层的划分
空间复杂度:O(n),合并n个元素需要一个大小为n的辅助空间
稳定性
稳定的排序算法,在比较和合并过程中,相同元素排序前后的相对次序不发生改变。
特点
- 归并排序的时间复杂度O(n*log n),这是基于比较的排序算法所能达到的最高效率
- 需要较多的辅助空间