归并排序的流程如下
第一步:对数列每次都进行切分,直到不能再切分
每次都对数列进行切分分组,直到每组的元素都只有一个,学过递归的话很容易想到这种重复性的动作用递归很容易实现。对于8个数的数列,切分三次直到第四层就不能再切分了。
第二步:进行归并
对于上述的数列,当每组的元素都是一个,也就是说每组的都是有序的了,递归返回,返回到倒数第二层。此时可对每组元素进行归并
再进行归并
可以看到归并将每组的数排成有序的了,最后我们将最后两组数再进行归并即可。
此时,数列有序,归并结束
编程思路
对于每次把数列按半切分分组,对分好的组进行归并这个流程可以用递归实现:
//归并过程
public static void merge(int arr[],int l,int mid,int r){
//归并排序辅助数组
int T[] = new int[r-l+1];
//T[0]到T[r-l]保存arr[l]到arr[r]的值
for(int i = l; i <= r; i++){
T[i-l] = arr[i];
}
//使用i指向分组1的第一个数的位置,j指向分组2的第一个数的索引
int i = l,j = mid+1;
//需要归并的数组位置是[l,r]
for(int k = l ; k <= r; k++){
if(i > mid){ //归并后,分组2还有元素,依次覆盖到原数组对应处
arr[k] = T[j-l];
j++;
}else if(j > r){ //归并后,分组1还有元素,依次覆盖到原数组对应处
arr[k] = T[i-l];
i++;
}else if(T[i-l] < T[j-l]){ //将分组中小的数覆盖到原数组对应处
arr[k] = T[i-l];
i++;
}else if(T[i-l] >= T[j-l]){
arr[k] = T[j-l];
j++;
}
}
}
T[]是辅助数组,用来存放两个需要进行归并的分组。在归并时,每次将两组中的最小的那个数依次放到原数组。
这个过程如下所示:
优化
public static void merge_sort(int arr[],int l,int r){
if(l >= r)
return;
/**对于数不是很多的情况下,可以使用插入排序代替归并来提高效率
* if(r - l <= 10){
* 插入排序;
* return;
* }
*/
int mid = (l+r) / 2;
merge_sort(arr,l,mid);
merge_sort(arr,mid+1,r);
//优化,归并时左边的最后一个数已经是小于右边第一个数时,可以不用归并了
if(arr[mid] > arr[mid+1]){
merge(arr,l,mid,r);
}
}