归并排序介绍

归并排序(Merge Sort)就是利用归并的思想实现的排序方法。它的原理是假设初始序列含有fn个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n2\frac{n}{2}2n​]([x]表示不小于x的最小整数)个长度为2或1的有序子序列;在两两归并,…,如此重复,知道得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。

根据具体的实现,归并排序包括"从上往下"和"从下往上"2种方式。

下面的图片很清晰的反映了"从下往上"和"从上往下"的归并排序的区别。

从上往下

代码实现:

  1. /**
  2. * 从上到下
  3. * @param elem
  4. * @param start
  5. * @param end
  6. */
  7. public void mergeSortUp2Down(int[] elem, int start, int end) {
  8. if(elem == null || start >= end) {
  9. return;
  10. }
  11. int mid = (start + end) / 2;
  12. mergeSortUp2Down(elem, start, mid);
  13. mergeSortUp2Down(elem, mid + 1, end);
  14. merge(elem, start, mid, end);
  15. }
  16. public void merge(int[] elem, int start, int mid, int end) {
  17. int[] temp = new int[end - start + 1];
  18. int i = start;
  19. int j = mid + 1;
  20. int k = 0;
  21. while(i <= mid && j <= end) {
  22. if(elem[i] < elem[j]) {
  23. temp[k++] = elem[i++];
  24. }
  25. else {
  26. temp[k++] = elem[j++];
  27. }
  28. }
  29. while(i <= mid) {
  30. temp[k++] = elem[i++];
  31. }
  32. while(j <= end) {
  33. temp[k++] = elem[j++];
  34. }
  35. for (i = 0; i < k; i++) {
  36. elem[start + i] = temp[i];
  37. }
  38. temp = null;
  39. }

从上往下的思路如图所示:

从下往上

代码实现:

  1. /**
  2. * 从下到上
  3. * @param elem
  4. */
  5. public void mergeSortDown2Up(int[] elem) {
  6. if(elem == null) return;
  7. for (int i = 1; i < elem.length; i *= 2) {
  8. mergeGroups(elem, elem.length, i);
  9. }
  10. }
  11. public void mergeGroups(int[] elem, int len, int gap) {
  12. int i;
  13. for (i = 0; i + 2 * gap -1 < len; i += (2 * gap)) {
  14. merge(elem, i, i + gap -1, i + 2 * gap -1);
  15. }
  16. if(i + gap -1 < len - 1) {
  17. merge(elem, i, i + gap - 1, len - 1);
  18. }
  19. }

归并排序的复杂度分析

归并排序的时间复杂度是O(nlog⁡\loglogn)。

假设被排序的数列中有n个元素。遍历一趟的时间复杂度是O(n),需要遍历多少次呢?归并排序的形式就是一棵二叉树,它需要遍历的次数就是二叉树的深度,而根据完全二叉树的性质可以得知可以得出它的时间复杂度是O(nlog⁡\loglogn)。

由于归并怕徐在归并过过程中需要与原始记录序列同样数量的存储空间存放归并结果以及递归时深度为log2log_2log2​n的栈空间,所以空间复杂度为O(n + log⁡\loglogn)

归并排序是稳定的算法,它满足稳定算法的定义。

归并排序的非递归实现

非递归的思想和递归一样,均为先分解后合并,非递归的重点在于如何确定并合理的分解待排序数组。

对于非递归来讲,切分的不向递归从大到小,非递归实际上从一开始构建算法的时候都从小到大。

第一次切分排序就确定最小单位为1个数字,将2个数字组合为一组。



第二次切分排序确定为2个数字,将4个数字组合为一组。



第三次切分排序确定为4个数字,将8(7)个数字组合为一组。



也就是说非递归归并排序中分解的依据为:从切分的长度为1开始,一次归并变回原来的2倍。每完成一次归并则 gap = gap * 2。

  1. /**
  2. * 非递归
  3. * @param elem
  4. */
  5. public void mergeSortNon(int[] elem) {
  6. int gap = 1;
  7. while(gap <= elem.length) {
  8. for (int i = 0; i + gap < elem.length; i += (gap * 2)) {
  9. int start = i, mid = i + gap -1, end = i + 2 * gap -1;
  10. if(end > elem.length - 1) {
  11. end = elem.length - 1;
  12. }
  13. merge(elem, start, mid, end);
  14. }
  15. gap *= 2;
  16. }
  17. }

完整代码:

  1. public class Test {
  2. /**
  3. * 从上到下
  4. * @param elem
  5. * @param start
  6. * @param end
  7. */
  8. public void mergeSortUp2Down(int[] elem, int start, int end) {
  9. if(elem == null || start >= end) {
  10. return;
  11. }
  12. int mid = (start + end) / 2;
  13. mergeSortUp2Down(elem, start, mid);
  14. mergeSortUp2Down(elem, mid + 1, end);
  15. merge(elem, start, mid, end);
  16. }
  17. /**
  18. * 从下到上
  19. * @param elem
  20. */
  21. public void mergeSortDown2Up(int[] elem) {
  22. if(elem == null) return;
  23. for (int i = 1; i < elem.length; i *= 2) {
  24. mergeGroups(elem, elem.length, i);
  25. }
  26. }
  27. public void mergeGroups(int[] elem, int len, int gap) {
  28. int i;
  29. for (i = 0; i + 2 * gap -1 < len; i += (2 * gap)) {
  30. merge(elem, i, i + gap -1, i + 2 * gap -1);
  31. }
  32. if(i + gap -1 < len - 1) {
  33. merge(elem, i, i + gap - 1, len - 1);
  34. }
  35. }
  36. /**
  37. * 非递归
  38. * @param elem
  39. */
  40. public void mergeSortNon(int[] elem) {
  41. int gap = 1;
  42. while(gap <= elem.length) {
  43. for (int i = 0; i + gap < elem.length; i += (gap * 2)) {
  44. int start = i, mid = i + gap -1, end = i + 2 * gap -1;
  45. if(end > elem.length - 1) {
  46. end = elem.length - 1;
  47. }
  48. merge(elem, start, mid, end);
  49. }
  50. gap *= 2;
  51. }
  52. }
  53. public void merge(int[] elem, int start, int mid, int end) {
  54. int[] temp = new int[end - start + 1];
  55. int i = start;
  56. int j = mid + 1;
  57. int k = 0;
  58. while(i <= mid && j <= end) {
  59. if(elem[i] < elem[j]) {
  60. temp[k++] = elem[i++];
  61. }
  62. else {
  63. temp[k++] = elem[j++];
  64. }
  65. }
  66. while(i <= mid) {
  67. temp[k++] = elem[i++];
  68. }
  69. while(j <= end) {
  70. temp[k++] = elem[j++];
  71. }
  72. for (i = 0; i < k; i++) {
  73. elem[start + i] = temp[i];
  74. }
  75. temp = null;
  76. }
  77. public static void main(String[] args) {
  78. Test t = new Test();
  79. int[] elem = {80,30,60,40,20,10,50,70};
  80. t.mergeSortUp2Down(elem, 0, elem.length - 1); //从上到下
  81. // t.mergeSortDown2Up(elem); //从下到上
  82. // t.mergeSortNon(elem); //非递归
  83. for (int i = 0; i < elem.length; i++) {
  84. System.out.print(elem[i] + ", ");
  85. }
  86. }
  87. }

参考:

https://www.cnblogs.com/skywang12345/p/3602369.html

https://www.cnblogs.com/yulinfeng/p/7078661.html?utm_source=itdadao&utm_medium=referral

《大话数据结构》

补充说明

最近在做一道《剑指Offer》上的一道算法的时候,用到了归并排序,因此加深了我对归并排序的核心排序(merge())方法加深了理解:



其实核心的排序就是设立三个指针P1, P2, P3。P3指针所指的数组就是两个数组合并且排序后的数组。每次比较P1和P2指针所指的元素:

  • 如果P1<P2,那么将P1所指的元素赋给P3所指的数组节点,并将P1和P3的指针后移一位,P2指针不动。
  • 如果P1>P2,那么将P2所指的元素赋给P3所指的数组节点,并将P2和P3的指针后移一位,P1指针不动。
  • 当P1或P2其中某一个指针指到最后了,那么也就是说两个数组比较排序已经完成,但是其中一个数组还“剩下”一部分元素(注意剩下的这些元素是有序的且剩下元素中最小的元素也大于新数组的最大元素),将剩下这部分的元素放在新数组后面。

归并排序的理解和实现(Java)的更多相关文章

  1. Atitit  深入理解命名空间namespace  java c# php js

    Atitit  深入理解命名空间namespace  java c# php js 1.1. Namespace还是package1 1.2. import同时解决了令人头疼的include1 1.3 ...

  2. 理解和解决Java并发修改异常ConcurrentModificationException(转载)

    原文地址:https://www.jianshu.com/p/f3f6b12330c1 理解和解决Java并发修改异常ConcurrentModificationException 不知读者在Java ...

  3. 深入理解和探究Java类加载机制

    深入理解和探究Java类加载机制---- 1.java.lang.ClassLoader类介绍 java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字 ...

  4. 深入理解什么是Java泛型?泛型怎么使用?【纯转】

    本篇文章给大家带来的内容是介绍深入理解什么是Java泛型?泛型怎么使用?有一定的参考价值,有需要的朋友可以参考一下,希望对你们有所助. 一.什么是泛型 “泛型” 意味着编写的代码可以被不同类型的对象所 ...

  5. 多校第五场 归并排序+暴力矩阵乘+模拟+java大数&amp;记忆化递归

    HDU 4911 Inversion 考点:归并排序 思路:这题呀比赛的时候忘了知道能够用归并排序算出逆序数,可是忘了归并排序的实质了.然后不会做-- 由于看到题上说是相邻的两个数才干交换的时候.感觉 ...

  6. [转载] 深入理解Android之Java虚拟机Dalvik

    本文转载自: http://blog.csdn.net/innost/article/details/50377905 一.背景 这个选题很大,但并不是一开始就有这么高大上的追求.最初之时,只是源于对 ...

  7. 如何理解和使用Java package包

    Java中的一个包就是一个类库单元,包内包含有一组类,它们在单一的名称空间之下被组织在了一起.这个名称空间就是包名.可以使用import关键字来导入一个包.例如使用import java.util.* ...

  8. 深入理解JVM(6)——Java内存模型和线程

    Java虚拟机规范中定义了Java内存模型(Java Memory Model,JMM)用来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果(“即Ja ...

  9. 理解JVM之Java内存区域

    Java虚拟机运行时数据区分为以下几个部分: 方法区.虚拟机栈.本地方法栈.堆.程序计数器.如下图所示: 一.程序计数器 程序计数器可看作当前线程所执行的字节码行号指示器,字节码解释器工作时就是通过改 ...

随机推荐

  1. KbmMW 4.30.00 发布

    今天早上,KbmMW发布了4.30.00 版,这个版本开始支持XE4 的WIN/WIN64/OSX. 暂时不支持ios开发,同时加强了通过JSON 的对象序列化.还有就是解决了我提交的几个有关 汉字处 ...

  2. Word插入圆圈数字

    https://wenku.baidu.com/view/3260a2f0a1c7aa00b52acb5a.html Word 中在对应位置输入四位字符,选中字符(如:选中 2473 ),按 Alt+ ...

  3. day15(mysql 的多表查询,事务)

    mysql之多表查询 1.合并结果集 作用:合并结果集就是把两个select语句查询的结果连接到一起! /*创建表t1*/ CREATE TABLE t1( a INT PRIMARY KEY , b ...

  4. 常用算法 (JS实现)

    全排序列 function swap(array,a,b){ var m=array[a]; array[a]=array[b]; array[b]=m; } function full_sort(a ...

  5. Hdu1342 Lotto 2017-01-18 17:12 44人阅读 评论(0) 收藏

    Lotto Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other) Total Submiss ...

  6. NLTK之WordNet 接口

    WordNet是面向语义的英语词典,类似于传统字典.它是NLTK语料库的一部分,可以被这样调用: 更简洁的写法: 1.单词 查看一个单词的同义词集用synsets(); 它有一个参数pos,可以指定查 ...

  7. SQL Server 2008 清空删除日志文件(瞬间日志变几M)

    sql 在使用中每次查询都会生成日志,但是如果你长久不去清理,可能整个硬都堆满哦,笔者就遇到这样的情况,直接网站后台都进不去了.下面我们一起来学习一下如何清理这个日志吧 SQL2008清空删除日志: ...

  8. 挂载Linux云主机硬盘到本地计算机

      现在移动硬盘已经是每个人的生活必需品了,当然网络也是我们生活的必需品,我们现在就是要用网络存储代替硬盘存储,当然再实际使用过程中需要考虑到以下两个问题: 网络延迟 云主机磁盘IO   以上两个关键 ...

  9. Vue2.5 Web App 项目搭建 (TypeScript版)

    参考了几位同行的Blogs和StackOverflow上的许多问答,在原来的ng1加TypeScript以及Webpack的经验基础上,搭建了该项目,核心文件如下,供需要的人参考. package.j ...

  10. laravel字段自增/自减

    DB::table('users')->increment('votes');DB::table('users')->increment('votes', 5);DB::table('us ...