算法分析(4)-排序-归并排序

1简单释义

less和exch可以查看上一章:算法分析(3)-简单排序总结(选择,插入,希尔含图解)——Comparable接口

归并采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。归并排序是一种稳定的排序方法。

  • 归并排序能够保证将任意长度为$N$的数组排序所需时间和$NlogN$成正比;
  • 归并排序的主要缺点是其所需的额外空间与$N$成正比。

2 自顶向下的归并排序

2.1图解

自顶向下其实是先完成左侧数组然后完成右侧数组的排序规则:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.2 原地归并抽象方法的解释

实现归并的一种方式就是直接将两个有序数组归并到第三个数组中,图示如下:

  1. 首先将左右序列排序好的数组$a[k]$所有元素复制到$aux[k]$中:

在这里插入图片描述
2. 然后将$aux[k]$中的元素归并到$a[k]$中。

  • 如果右方元素较小将$aux[j]$中的元素归并到$a[k]$中,
  • 如果左方元素较小将$aux[i]$中的元素归并到$a[k]$中。
    在这里插入图片描述
    在这里插入图片描述
    左边用尽直接将后半部分$aux[j]$复制到$a[k]$

在这里插入图片描述
原地归并算法如下:
这里的$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);//归并过程,见上方抽象方法说明
    }
}

其实$sort$的的作用只是对$merge$执行的顺序完成合理调用。
在这里插入图片描述
这里直接给出对于长度为$N$的数组,自顶向下的归并排序的比较次数:$1/2NlgN$~$NlgN$
访问数组的次数(最多):$6NlgN$
关于算法规模的计算以及数学分析的相关证明,会在本专栏之后进行更新。

2.4自顶向下的归并排序优化建议

代码后期会在github上更新,目前博主的git在商用,暂时不放优化代码

  1. 对小规模子数组使用插入排序
  2. 对子数组随时检测是否有序
  3. 不讲元素复制到辅助数组

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$的数组,自顶向下的归并排序的比较次数:$1/2NlgN$~$NlgN$
访问数组的次数(最多):$6NlgN$

posted @ 2021-02-24 16:38  算法坞  阅读(202)  评论(0编辑  收藏  举报