@[TOC](考研之数据结构032_算法排序_归并排序(Merge sort))
一、归并的思想(升序)
1、二路归并
- 归并:把有两个或者多个有序的序列,合并成一个有序序列。
例如:将有序数组a,b进行合并到c数组中。
- 设置三个指针i,j,k分别指向abc三个数组的第一个元素。
- 将对比i,j指向的两个元素,将更小的元素放到C数组中,并将i/j和k加加(指向下一个元素)。
- 一直重复第二项,直到i/j超出数组的下标范围,表明i/j所代表的的a/b数组已经完成了合并。将另外一个未合并完的数组,将剩下的元素不再进行对比(退出循环),将剩下的全部加到总表C中。
2、多路归并
例如:四路归并,就是四个有序数列的数组中,从中选出最小的元素放到总数组中。由于有四个数组,所以至少对比三次。
结论:m归并:每选出一个元素需要对比关键字m-1次
3、归并排序(手算模拟)(算法思想)
== 核心操作:把数组内的两个有序序列归并为一个==
- 在内部排序中,归并排序是使用的2路归并。
- 给出一个初始序列,刚开始会把初始序列当中每一个单独的元素,都看作是一个个独立的排好序的部分(每个部分都是一个元素,所以肯定是有序的)。
- 所以第一次排序会把两个相邻的两个部分(元素),进行二路归并。
- 第二趟归并是基于第一趟归并的结果,也就是相邻的两个部分(两个元素)进行归并排序。
二、归并排序的代码
1、二路归并代码
进行一步步分析:
1. 申请一个新数组B,大小是数组的个数。
将B数组,视为两部分:
前部分是low-mid
后部分是:mid+1 - high
对前后两部分进行判断比较大小。
2. 归并函数:参数(数组,low,mid,high)
3. 将要排序的数组A复制给数组B。(在B中排序,一个个赋值给A数组。)
4. 主要内容:
一个要排序的数组,有7个元素,前四个和后三个是排好序的。那么:
i=low 指向的是第一个数组的第一个元素
mid 指向的是第一个数组的最后一个元素,整个数组的中间元素
j=mid+1 指向的是第二个数组的第一个元素
high 指向的是第二个数组的最后一个元素。
5. for(i=low,j=mid+1,k=i;i<=mid && j<=high;k++)
将数组视为两个排好序的数组,前部分和后半部分。
1.初始化:初始化头和尾
2.判断:是否超出A和B数组的范围
3.每一次循环,都会在新数组中添加元素,所以K++
6.判断
第一个数组的第i个元素<=第二个数组的第j个元素:
将i++元素 赋值给 数组[k] i++先用后加一
else 将j++元素 赋值给 数组[k]
7. while(i/j<=mid/high) A[k++]=B[i/j++] 先用后加!!
两个数组,总会有其中一个数组会先超出数组范围,由于上面判断的条件是:
当有超出范围的数组,循环会跳出。
所以:将未超出数组范围的有序序列,直接复制到要排序的数值当中。
二路排序的实际代码:
int *B=(int *)malloc(n* sizeof(int))
//A[low-mid] 和 A[mid+1 - high] 各自有序,将两部分归并
void Merge(int A[],int low,int mid,int high){
int i,j,k;
for(k=low,k<=high;k++)
B[k]=A[k]; //从A复制到B数组
for(i=low,j=mid+1,k=i; i<=mid+1 && j<=high;k++){
if(B[i]<=B[j])
A[k]=B[i++];
else
A[k]=B[j++];
}// for
//将剩下的元素放到数组A中。
while(i<=mid) A[k++]=B[i++];
while(j<=high) A[k++]=B[j++];
}
2. 归并排序代码
- 要对A数组进行归并排序,用Low和high来指定范围。
- 如果l<h在范围内,那么就将中间划分出mid。
- 将从中间mid拆分为左右两个部分:左:low-mid 右:mid+1 - high
- 将左右部分进行递归的归并排序。mergeSort函数
- 直到分为一个元素为一个数组(n个元素,那么就可以设想为有n个数组,实际就一个数组),进行merge函数排序。将该排序范围的数组A 复制到B数组,在B数组中进行对比,然后赋值给A数组。
- 在两个元素为一组,进行merge函数排序。…最后一组是:两个数组进行一次排序。
三、时间复杂度和空间复杂度
四、稳定性:可以稳定
在对两个有序的子序列,进行对比的时候,如果两边同时出现了,相等的情况,那么会优先的让靠左部分的子序列,先合并进去。所以是稳定的。