“归并”的含义是将两个或者两个以上的有序表组合成一个新的有序表。

假设待排序表含有n个元素,则可以看成是n个有序的子表,每个子表的长度为1,然后两两归并,得到(n/2)或者(n/2+1)个长度为2或1的有序表;再两两归并。。。

如此重复,直到合并成一个长度为n的有序表为止。

这种方法称为二路归并排序算法。

直接上代码:

package com.mergeSort;

public class MergeSort {

    //归并排序的对外公共接口
public static void mergeSort(int arr[],int n){
subMergeSort(arr,0,n-1);
} //实现归并排序
private static void subMergeSort(int[] arr, int l, int r) { //当l=r时即将待排序序列二分到每组只有一个元素时,对于一个元素来说,它自己就是有序的
if(l>=r)
return;
int mid=(l+r)/2;
subMergeSort(arr,l,mid);//归并排序前半段
subMergeSort(arr,mid+1,r);//归并排序后半段
//将两段有序序列归并成一段有序序列
mergeSortCore(arr,l,mid,r); } //将两段有序序列归并成一段有序序列
//对arr[l...mid]和arr[mid+1,r]两部分进行归并
private static void mergeSortCore(int[] arr, int l, int mid, int r) { //开辟出辅助数组
int[] a=new int[r-l+1];
//将原数组全部元素复制到辅助数组
for(int i=l;i<=r;i++)
a[i-l]=arr[i]; //a数组是从0开始的,因此和arr数组存在l个偏移量
//定义两个指针,分别指向两段有序序列的起始位置
int j=mid+1;
int i=l;
//用k作指针将排序后的数组元素重新放到原数组arr[l...r]中
for(int k=l;k<=r;k++){
//当前半段都放入arr中而后半段还没有遍历完
if(i>mid){
arr[k]=a[j-l];
j++;
}else
//当后半段都放入arr中而前半段还没有遍历完
if(j>r){
arr[k]=a[i-l];
i++;
}
else
//若两段都没遍历完,那么就比较一下
if(a[i-l]<a[j-l]){
arr[k]=a[i-l];
i++; //别忘了每搞定一个元素,指针的后移
}
else
{
arr[k]=a[j-l];
j++;
}
}
} public static void main(String[] args){
int[] arr=new int[]{10,9,8,7,6,5,4,3,2,1};
mergeSort(arr,10);
for(int i=0;i<arr.length;i++)
System.out.print(arr[i]+" ");
} }

注意:

1)归并排序需要一个新建一个辅助数组;

2)本段代码中排序数组的每段都是两头包括的闭区间,例如arr[l...mid]。当然也可以定义成一头开一头闭的区间,例如arr[l...mid+1),只不过相关代码需要做相应的改动。

3)代码中划下划线的部分,即 int mid=(l+r)/2; 在数据量很大的时候(即l和r很大的时候),有可能会造成mid的值的溢出。

4)该排序算法的时间复杂度为O(nlog2n)。

5)是稳定的。

对二路排序算法的优化:


1)在上面代码中实现归并排序的subMergeSort方法中有这么一个片段:

subMergeSort(arr,l,mid);//归并排序前半段
subMergeSort(arr,mid+1,r);//归并排序后半段
//将两段有序序列归并成一段有序序列
mergeSortCore(arr,l,mid,r);

这段代码中,我们对每一次单独排序完的两段都进行了一次mergeSortCore()方法的归并操作。

但是因为这两段各自内部都是有序的,那么当arr[mid]<=arr[mid+1]时,我们就没有必要再去操作mergeSortCore()方法,

因此我们可以加个判断语句,来省掉多余的操作。

        subMergeSort(arr,l,mid);//归并排序前半段
subMergeSort(arr,mid+1,r);//归并排序后半段 //当arr[mid]<=arr[mid+1]时证明整个arr数组就已经有序了,那么此时根本不需要调用mergeSortCore方法
//只有当arr[mid]>arr[mid+1]时,我们才有必要执行mergeSortCore方法
if(arr[mid]>arr[mid+1]) //将两段有序序列归并成一段有序序列
mergeSortCore(arr,l,mid,r);

2)几乎对于所有的高级排序算法,有一种通用的优化,那就是在递归到底的情况下,可以使用直接插入排序进行优化。

当递归几乎到底时,每一组只有有限数量的元素,而元素越少,那么倾向于有序的几率越大。虽然直接插入排序的时间复杂度是O(n2),但是我们知道在元素趋向于有序时,它甚至可以高效运行使得时间复杂度达到O(n)。

同时,对于时间复杂度前面的系数大小,直接插入排序是比归并排序小的。

因此,当n小到一定程度,直接插入排序会比归并排序要快。

所以对于上面代码中实现归并排序的subMergeSort方法中的一个片段:

if(l>=r)
return;

改为:

if(r-l<=15){
insertionSort(arr,l,r);
return;
}

其中,insertionSort()方法是已经定义的实现闭区间直接插入排序的方法。

数字15也可以设置为其他数值。

从以上代码的执行过程来看,这是一种自上而下的归并排序算法。

对于自下而上的归并排序算法:


public static void mergeSortBtoU(int[] arr,int n){

        //自下而上的归并排序算法
//第一层循环对参与mergeSortCore的元素个数进行遍历,第一次为1个,第二次为2个,第三次为4个,第四次为8个。。。。
//从每一个单个元素开始作为归并小组进行归并,每一次归并成的结果是归并前原小组规模的二倍(即sz+=sz)
//当归并小组规模达到整个待排序数组大小的时候(即sz<=n),完成归并排序算法
for(int sz=1;sz<=n;sz+=sz)
//i为每一次进行mergeSortCore排序的元素的起始位置(每次进行两组元素的归并排序)
//每一次归并两组,每组的大小为sz,所以每次i的移动幅度为两个sz(即i+=sz+sz)
//用第二部分起始位置(i+sz)小于n,来限制每次归并时第二部分的存在,同时也保证了(i+sz-1)不会出现越界问题
for(int i=0;i+sz<n;i+=sz+sz)
//对arr[i...i+sz-1]和arr[i+sz...i+2*sz-1]进行归并
//对于将要进行归并的第二部分可能出现不足sz大小的情况,即(i+sz+sz-1可能出现越界问题)
//故用Math的min()方法进行限制
MergeSort.mergeSortCore(arr, i, i+sz-1, Math.min(i+sz+sz-1,n-1));
}

对此,也可进行优化操作。

另,对于自下而上的归并排序算法,没有使用数组中一个重要的性质:使用索引直接确定元素位置。因此,这种算法可以以时间复杂度为O(nlog2n)的速度对链表进行排序。

二路归并算法的java实现的更多相关文章

  1. 常见排序算法总结 -- java实现

    常见排序算法总结 -- java实现 排序算法可以分为两大类: 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序. 线性时间 ...

  2. 十大经典排序算法(java实现、配图解,附源码)

    前言: 本文章主要是讲解我个人在学习Java开发环境的排序算法时做的一些准备,以及个人的心得体会,汇集成本篇文章,作为自己对排序算法理解的总结与笔记. 内容主要是关于十大经典排序算法的简介.原理.动静 ...

  3. 对一致性Hash算法,Java代码实现的深入研究

    一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法.一致性 ...

  4. 常见排序算法(附java代码)

    常见排序算法与java实现 一.选择排序(SelectSort) 基本原理:对于给定的一组记录,经过第一轮比较后得到最小的记录,然后将该记录与第一个记录的位置进行交换:接着对不包括第一个记录以外的其他 ...

  5. 几大排序算法的Java实现

    很多的面试题都问到了排序算法,中间的算法和思想比较重要,这边我选择了5种常用排序算法并用Java进行了实现.自己写一个模板已防以后面试用到.大家可以看过算法之后,自己去实现一下. 1.冒泡排序:大数向 ...

  6. 8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,循环控制及其优化

    上两篇博客 8皇后以及N皇后算法探究,回溯算法的JAVA实现,递归方案 8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,数据结构“栈”实现 研究了递归方法实现回溯,解决N皇后问题,下面我们来 ...

  7. 7种基本排序算法的Java实现

    7种基本排序算法的Java实现 转自我的Github 以下为7种基本排序算法的Java实现,以及复杂度和稳定性的相关信息. 以下为代码片段,完整的代码见Sort.java 插入排序 /** * 直接插 ...

  8. 利用朴素贝叶斯算法进行分类-Java代码实现

    http://www.crocro.cn/post/286.html 利用朴素贝叶斯算法进行分类-Java代码实现  鳄鱼  3个月前 (12-14)  分类:机器学习  阅读(44)  评论(0) ...

  9. 【LeetCode-面试算法经典-Java实现】【053-Maximum Subarray(最大子数组和)】

    [053-Maximum Subarray(最大子数组和)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Find the contiguous subarray w ...

随机推荐

  1. Group By Rollup

    Rollup与group by组合使用,可对分组结果进行进一步的汇总. 创建数据表 select * from emp_test (1) rollup单个字段 如按照country字段进行分组,并在最 ...

  2. Android中getDrawable和getColor过时的替代方法

    版权声明:本文为博主原创文章,未经博主允许不得转载. 前言 Android SDK 升级到 23 之后,getDrawable和getColor方法提示过时. 解决方案 getResources(). ...

  3. python练习六—简单的论坛

    进行简单的web应用之后,接下来就应该学习python连接数据库,这个练习就是在上个练习的基础上将信息保存到数据库,这个联系也没有什么特别的,有之前java web的经验的话,很好理解,主要还是一个M ...

  4. Linux常用命令之链接命令和权限管理命令

    目录 1.链接命令 一.生成链接文件命令:ln 2.权限管理命令3.总结 一.更改文件或目录权限命令:chmod 二.改变文件或目录所有者命令:chown 三.改变文件或目录所属组命令:chgrp 四 ...

  5. datatables 配套bootstrap样式使用小结(2) ajax篇

    距离上一篇有点时间了,周末做了伴郎参加了一个土豪同学的婚礼. 上一篇是介绍一下基本的用法,基本的原理是取出所有的数据,然后调用$.datatables(option)方法格式化,这种方法显然不太科学, ...

  6. TCP/IP协议学习(一)

    一.网络模型 OSI七层模型:自上至下依次是 应用层,表示层,会话层,传输层,网络层,数据链路层,物理层 应用层:具体的应用协议如HTTP.SMTP.FTP.TELNET.DNS等 表示层:针对数据格 ...

  7. Go 标准库 http.FileServer 实现静态文件服务

    http.FileServer 方法属于标准库 net/http,返回一个使用 FileSystem 接口 root 提供文件访问服务的 HTTP 处理器.可以方便的实现静态文件服务器. http.L ...

  8. go基础系列:数组

    了解Python.Perl.JavaScript的人想必都知道它们的数组是动态的,可以随需求自动增大数组长度.但Go中的数组是固定长度的,数组一经声明,就无法扩大.缩减数组的长度.但Go中也有类似的动 ...

  9. zepto 事件分析3(add函数)

    在上一篇的分析中,最后$.on方法返回了一个add方法函数的执行,在这里先看一下其代码: function add(element, events, fn, data, selector, deleg ...

  10. Spring Boot入门(13)自制音乐平台

      经过笔者这几天的辛勤劳作(其实就是苦逼地码代码),一个新的网站已经上线啦!该网站是用Spring Boot工具写的,主要实现的功能如下: 根据歌曲名称和音乐平台搜索歌曲,并实现歌曲的在线播放: 歌 ...