归并排序Merge sort
1简单释义
less和exch可以查看上一章:算法分析(3)-简单排序总结(选择,插入,希尔含图解)——Comparable接口
归并采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。归并排序是一种稳定的排序方法。
- 归并排序能够保证将任意长度为 N N N的数组排序所需时间和 N l o g N NlogN NlogN成正比;
- 归并排序的主要缺点是其所需的额外空间与 N N N成正比。
2 自顶向下的归并排序
2.1图解
自顶向下其实是先完成左侧数组然后完成右侧数组的排序规则:
2.2 原地归并抽象方法的解释
实现归并的一种方式就是直接将两个有序数组归并到第三个数组中,图示如下:
- 首先将左右序列排序好的数组 a [ k ] a[k] a[k]所有元素复制到 a u x [ k ] aux[k] aux[k]中:
2. 然后将
a
u
x
[
k
]
aux[k]
aux[k]中的元素归并到
a
[
k
]
a[k]
a[k]中。
- 如果右方元素较小将 a u x [ j ] aux[j] aux[j]中的元素归并到 a [ k ] a[k] a[k]中,
- 如果左方元素较小将
a
u
x
[
i
]
aux[i]
aux[i]中的元素归并到
a
[
k
]
a[k]
a[k]中。
左边用尽直接将后半部分 a u x [ j ] aux[j] aux[j]复制到 a [ k ] a[k] a[k]
原地归并算法如下:
这里的
l
e
s
s
less
less可以查看上一章算法分析(3)-简单排序总结(选择,插入,希尔含图解)
//lo第一个元素,hi最后一个元素
private static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi)
{
for (int k = lo; k <= hi; k++)
aux[k] = a[k];
int i = lo, j = mid+1;
for (int k = lo; k <= hi; k++)
{
if (i > mid) a[k] = aux[j++];//左边用尽直接将后半部分复制到a[k]
else if (j > hi) a[k] = aux[i++];//右边用尽直接将前半部分复制到a[k]
else if (less(aux[j], aux[i])) a[k] = aux[j++];//右方元素较小,复制到a[k],
else a[k] = aux[i++];//左方元素较小,复杂到a[k]
}
}
使用以上的抽象方法可以使用递归的方式完成递归排序。
2.3自顶向下的归并排序
首先上代码:
//递归的实现归并排序
public class Merge {
private static Comparable[] aux;//归并所需的辅助数组
public class void sort(Comparable[] a)
{
aux=new Comparable[a.length];//为辅助数组分配空间
sort(a,0,a.length-1);//递归过程
}
//排序
private static void sort(Comparable[] a, int lo, int hi) {
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
sort(a,lo, mid);//排序左侧
sort(a,mid + 1, hi);//排序右侧
merge(a,lo, mid, hi);//归并过程,见上方抽象方法说明
}
}
其实
s
o
r
t
sort
sort的的作用只是对
m
e
r
g
e
merge
merge执行的顺序完成合理调用。
这里直接给出对于长度为
N
N
N的数组,自顶向下的归并排序的比较次数:
1
/
2
N
l
g
N
1/2NlgN
1/2NlgN~
N
l
g
N
NlgN
NlgN
访问数组的次数(最多):
6
N
l
g
N
6NlgN
6NlgN
关于算法规模的计算以及数学分析的相关证明,会在本专栏之后进行更新。
2.4自顶向下的归并排序优化建议
代码后期会在github上更新,目前博主的git在商用,暂时不放优化代码
- 对小规模子数组使用插入排序
- 对子数组随时检测是否有序
- 不讲元素复制到辅助数组
3 自底向上的归并排序
3.1图解
自底向上其实就是从所有数组中最小分组进行归并排序
首先进行分解(这里的分解其实使用递归即可):
当分解到最简元素时进行原地归并:
3.2代码
//递归的实现归并排序
public class Merge {
private static Comparable[] aux;//归并所需的辅助数组
public static void sort(Comparable[] a)
{
int N=a.length;
aux=new Comparable[N];//为辅助数组分配空间
for(int sz=1;sz<N;sz=sz+sz)
for(int lo=0;lo<N-sz;lo+=sz+sz)
merge(a,lo,lo+sz-1,Math.min(lo+sz+sz-1,N-1));//见上方抽象方法说明
}
}
这里直接给出对于长度为
N
N
N的数组,自顶向下的归并排序的比较次数:
1
/
2
N
l
g
N
1/2NlgN
1/2NlgN~
N
l
g
N
NlgN
NlgN
访问数组的次数(最多):
6
N
l
g
N
6NlgN
6NlgN