归并操作(merge),也叫归并算法,指的是将两个已经排序的序列合并成一个序列的操作。
归并排序算法依赖归并操作。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
归并排序在众多排序算法中既是稳定排序,效率也比较高,同时,归并排序不仅可以用于内排序,还可以用于外排序。

1.两个有序数列的合并

设两个有序数列放在同一向量中相邻的位置上:R[low..m],R[m+1..high],先将它们合并到一个局部的暂存向量 R1中,待合并完成后将 R1 复制回 R[low..high]中。
(1)合并过程
合并过程中,设置 i,j 和 p 三个指针,其初值分别指向这三个记录区的起始位置。

合并时依次比较 R[i]和 R[j]的关键字,取关键字较小的记录复制到 R1[p]中,然后将被复制记录的指针 i 或 j 加 1,以及指向复制位置的指针 p 加 1。

重复这一过程直至两个输入的子文件有一个已全部复制完毕,此时将另一非空的子文件中剩余记录依次复制到 R1 中即可。

(2)动态申请 R1
实现时,R1 是动态申请的,因为申请的空间可能很大,所以在工程上应用时,可能需要加入申请空间是否成功的处理。

2.归并排序的实现

(1)二路归并的思路
将数组划均分为两个子数组;
对两个字数组进行排序;
将排序好的两个字数组归并。
所谓 N路归并 是指将数组均分为N个子数组,将字数组排序后再归并。二路归并是归并排序的最一般的情况。


(2)归并排序实现的一般化过程
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
设定两个指针,最初位置分别为两个已经排序序列的起始位置
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针到达序列尾
将另一序列剩下的所有元素直接复制到合并序列尾

(3)实现过程

public class MergeSort {

	public static void main(String[] args){
int[] arr=new int[]{8,7,6,5,4,3,2,1};
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+",");
}
//想起引用传递和值传递的问题,数组是引用传递,不需要什么返回值
sort(arr,arr.length);
System.out.println("排序后");
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+",");
}
} public static void sort(int[] arr,int n){
if(arr==null || n<2){
return;//边界情况
}
divide(arr,0,n-1);
} /**
*
* @param arr 待排序数组
* @param left 待排序区间左侧下标
* @param right 待排序区间右侧下标
*/
public static void divide(int[] arr,int left,int right){ if(left>=right){
return;
}
int m=(left+right)/2;//从中间开始分成两个区间进行归并
divide(arr,left,m);//一直递归划分直到区间长度为1
divide(arr,m+1,right);
//开始逐级往上进行归并操作
binaryMerge(arr,left,m,right);//第一次进行merge操作的递归栈最深层此时是merge(arr,0,0,1)
} /**
* 对数组arr[left...right]位置进行递归的归并排序
* @param arr
* @param left
* @param rigtht
* @param temp
*/
public static void binaryMerge(int[] arr,int left,int m,int right){
/**
* 归并排序的时间复杂度为O(n),指的就是最大长度为n的临时数组
*/
int[] temp=new int[right-left+1];
int l=left;//
int r=m+1;//
int index=0;// while(l<=m && r<=right){
if(arr[l]<arr[r]){
temp[index]=arr[l];
index++;
l++;
}else{
temp[index]=arr[r];
index++;
r++;
}
} /**
* 交换完了如果哪一侧数组还有剩余,则全部赋值
* 实际的一次归并下面的两个while循环只会执行一个
* warn!不要漏了等于的情况!否则会每两个元素丢失一个元素
*/
while(l<=m){
temp[index]=arr[l];
index++;
l++;
} while(r<=right){
temp[index]=arr[r];
index++;
r++;
} /**
* 用临时数组的部分排序的序列全部替换待排序数组中对应的部分
* 这里应该用temp.length,不能用arr.length
*/
for(int i=0;i<temp.length;i++){
//注意是left,此时的l是已经变化了的
arr[left+i]=temp[i];
} } }

  

(4)时间和空间复杂度
归并排序的最好、最坏和平均时间复杂度都是O(nlogn),而空间复杂度是O(n),
比较次数介于(nlogn)/2和(nlogn)-n+1,赋值操作的次数是(2nlogn)。因此可以看出,归并排序算法比较占用内存,但却是效率高且稳定的排序算法。

3.空间复杂度为O(1)的归并排序

归并排序的空间复杂度为O(N),
通过优化可以把空间复杂度降为O(1),通过手摇算法,
但实际上通过手摇算法后,此时的时间复杂度会上升。

4.多路归并排序

归并排序的思想可以用于外排序。
外排序是相对内排序而言的,在常规的小规模排序过程中,都是直接在内存中对数据进行排序处理的,而对于数据量极大的排序问题,这种方式是不现实的。

这个时候就要通过外排序来进行,先将数据划分成多个规模能在内存中处理的子集,对各个子集排序后存放在临时的磁盘文件上,然后再将这些子集归并到输出文件中。

这个过程要使用到多路归并。

归并排序的分析与Java实现的更多相关文章

  1. java泛型中使用的排序算法——归并排序及分析

    一.引言 我们知道,java中泛型排序使用归并排序或TimSort.归并排序以O(NlogN)最坏时间运行,下面我们分析归并排序过程及分析证明时间复杂度:也会简述为什么java选择归并排序作为泛型的排 ...

  2. 性能分析之-- JAVA Thread Dump 分析综述

    性能分析之-- JAVA Thread Dump 分析综述       一.Thread Dump介绍 1.1什么是Thread Dump? Thread Dump是非常有用的诊断Java应用问题的工 ...

  3. 程序员必知的8大排序(四)-------归并排序,基数排序(java实现)

    程序员必知的8大排序(一)-------直接插入排序,希尔排序(java实现) 程序员必知的8大排序(二)-------简单选择排序,堆排序(java实现) 程序员必知的8大排序(三)-------冒 ...

  4. Python排序搜索基本算法之归并排序实例分析

    Python排序搜索基本算法之归并排序实例分析 本文实例讲述了Python排序搜索基本算法之归并排序.分享给大家供大家参考,具体如下: 归并排序最令人兴奋的特点是:不论输入是什么样的,它对N个元素的序 ...

  5. JVM源码分析之Java对象头实现

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 “365篇原创计划”第十一篇. 今天呢!灯塔君跟大家讲: JVM源码分析之Java对象头实现 HotSpot虚拟机中,对象在内存中的布局分为三 ...

  6. 各种排序算法的分析及java实现

    排序一直以来都是让我很头疼的事,以前上<数据结构>打酱油去了,整个学期下来才勉强能写出个冒泡排序.由于下半年要准备工作了,也知道排序算法的重要性(据说是面试必问的知识点),所以又花了点时间 ...

  7. (转)各种排序算法的分析及java实现

    转自:http://www.cnblogs.com/liuling/p/2013-7-24-01.html 排序一直以来都是让我很头疼的事,以前上<数据结构>打酱油去了,整个学期下来才勉强 ...

  8. 各种排序算法的分析及java实现 分类: B10_计算机基础 2015-02-03 20:09 186人阅读 评论(0) 收藏

    转载自:http://www.cnblogs.com/liuling/p/2013-7-24-01.html 另可参考:http://gengning938.blog.163.com/blog/sta ...

  9. HTTP POST请求报文格式分析与Java实现文件上传

    时间 2014-12-11 12:41:43  CSDN博客 原文  http://blog.csdn.net/bboyfeiyu/article/details/41863951 主题 HTTPHt ...

随机推荐

  1. shell与变量的声明的操作

    1.给命令起别名:alias 执行下面命令后,可以使用dir代替ls –l 命令,显示目录中的文件详细信息: 还可以用一个别名表示几个命令 的结合: 2.ps:显示当前登录会话的所有活动进程: 3.更 ...

  2. MyISAM和InnoDB的索引在实现上的不同

    1 MyISAM只把索引载入内存,数据缓存依赖于操作系统,InnoDB把索引和数据都载入内存缓冲 2 MyISAM数据库中的数据是按照插入的顺序保存,在每个索引节点中保存对应的数据行的地址,理论上说主 ...

  3. 代码重构-4 通用方法 用 static

    只要没有用到 this.变量/方法 的,都可以用static 原代码: private  string GetPeriodDesc(int lotteryPeriod) { return EnumHe ...

  4. mysql 存储过程中注意的问题

    OPEN cur; REPEAT FETCH cur INTO int_type, int_element_id, int_num, int_user_id; IF NOT _DONE THEN IF ...

  5. mongodb(基础用法)

    驱动和客户端库 https://mongodb-documentation.readthedocs.org/en/latest/ecosystem/drivers.html#id2 https://m ...

  6. UIScrollview自动布局,UIScrollviewAutolayoutDemo

    参考文档:http://www.cocoachina.com/ios/20150104/10810.html UIScrollviewAutolayoutDemo地址:http://pan.baidu ...

  7. 解读JSP的解析过程

    解读JSP的解析过程 互联网上,这方面的资料实在太少了,故把自己研究的一些结果公布出来. 首先,问大家几个问题,看大家能不能回答出来,或者在网上能不能找到答案: 1.page.include.tagl ...

  8. 使用Fabric进行crash收集统计

    主要是帮助自己记一下地址. 1 申请Crashlytics服务:http://try.crashlytics.com 2 下载Fabric客户端,帮助集成Crashlytics到自己的项目中:http ...

  9. Swift Tour 随笔总结 (4)

    Switch的一个例子: let vegetable = "red pepper" switch vegetable { case "celery": let ...

  10. iOS开发——底层OC篇&运行时常用

    运行时常用 什么是Runtime(前面的文章已经说的很清楚了,这里就简单的介绍一下) 我们写的代码在程序运行过程中都会被转化成runtime的C代码执行,例如[target doSomething]; ...