分治法:将原问题分解成几个规模较小的类似于原问题的子问题,递归的解决子问题,然后再合并这些问题的解来表示原问题的解。
分治模式基本步骤:分解 – 解决 – 合并。
归并排序就是遵循分治模式的。归并排序是按照基本步骤可分为:
分解:分解n个数的序列成各具n / 2个元素的两个序列
解决:使用归并排序递归地排序两个子序列
合并:合并两个已排序的子序列以产生已排序的答案。
理解:进行辅助过程merge()时,实际上如同桌面上有两堆已经排好序的牌面朝上的牌,从上面的两张中选择最小的一张放在输出堆(牌面朝下)中,之后再比较新的两张牌的大小,重复进行。如果有一堆已经选完,那么剩余的部分就可以全部放入输出堆了。但是这两个牌堆不能每次都检查他为空,所以设置了一个”哨兵“。
参考代码:
package cn.nuist.jr.experiment;
import java.util.Arrays;
public class Merge {
public static void main(String[] args) {
int [] A = {5, 2, 4, 7, 1, 3, 2, 6};
int i = 0, j = A.length - 1;
mergeSort(A, i, j);
System.out.println(Arrays.toString(A));
}
private static void mergeSort(int[] a, int start, int end) {//先递归分解数组,再合并数组 算是完成了归并排序。
if(start < end)
{
int mid = (start + end) / 2;
mergeSort(a, start, mid);
mergeSort(a, mid + 1, end);
merge(a, start, mid, end);
}
}
private static void merge(int[] a, int p, int q, int r) {//p 起点 r 终点
int i, j;
int n1 = q - p + 1;
int n2 = r - q;
int [] L = new int[n1 + 1];
int [] R = new int[n2 + 1];
for(i = 0; i < n1; i ++){
L[i] = a[p + i];
}
for(j = 0; j < n2; j ++){
R[j] = a[q + j + 1];
}
L[n1] = Integer.MAX_VALUE;
R[n2] = Integer.MAX_VALUE;//哨兵位置
i = 0; j = 0;
for(int k = p; k <= r; k ++){//注意此时需<= 否则会漏了数
if(L[i] <= R[j]){
a[k] = L[i];
i = i + 1;
}
else{
a[k] = R[j];
j = j + 1;
}
}
}
//Merge(A, q, p, r)//A表示数组,p, q, r 表示数组下标,且满足p<=q<r
//假设两个子数组为A[p...q] 和 A[q+1...r]都已经排好序,在进行合并。
//r - p + 1步
//伪代码:
//n1 = q - p + 1;
//n2 = r - q;
//let L [1, n1 + 1] and R[1, n2 + 1] be new arrays
//for i = 1 to n1
// L[i] = A[p + i -1]
//for j = 1 to n2
// R[j] = A[q + j]
//L[n1 + 1] = 无穷
//R[n2 + 1] = 无穷
//i = 1
//j = 1
//for k = p to r
// if L[i] <= R[j]
// A[k] = L[i]
// i = i + 1
// else
// A[k] = R[j]
// j = j + 1
}
参考截图(取自书上,比较方便):
结果截图:
归并排序的效率算是比较高的,如果数列长为N,将数列分成小数列需要logN步,每一步都是一个合并有序数列的过程,时间复杂度可记作O(N),一共为O(N*logN)。
参考:
《算法导论》
以上就是本篇的基本内容。如果有什么可以改进的地方或者您发现了错误,请指出,谢谢!