首页 > 其他分享 >归并排序详解

归并排序详解

时间:2024-07-11 15:29:19浏览次数:24  
标签:归并 right int arr 详解 数组 排序 left

文章目录

归并排序原理

归并排序,也是应用了分治的思想。将原数组分为两个子数组,左子数组和右子数组,两个子数组排好序后,通过拷贝的方法将两个数组边排序边合并。子数组可以继续分为子数组,直到子数组长度为1时天然有序。合并过程则是将拆分的子数组依次合并,直到合并到原数组的长度即排序完成。

排序演示1

原数组:[9,18,0,0,-9,17,0,3,0,10,19,8,4,-7],用归并排序为该数组排序
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

排序演示2

原数组:[-12,3,5,1,-1,-9,9,-12,-9,3,16,-10,4,-13,11],用归并排序为该数组排序
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

递归方式

	/**
	 * 通过递归的方式实现归并排序
	 * @param arr 要排序的数组
	 * @param left 数组的左边界
	 * @param right 数组的右边界
	 */
	public static void mergeSortByRec(int[] arr,int left,int right){
	    //递归出口,left=right时,说明递归到了长度为1的数组,天然有序
	    if(left==right) return;
	
	    //计算中间的索引值,(left+right)/2。
	    //  之所以这么优化,是为了避免left+right的值大于int最大范围导致出现bug
	    int mid = left + ((right-left)>>1);
	    //把左半边的数组排好序
	    mergeSortByRec(arr,left,mid);
	    //把右半边的数组排好序
	    mergeSortByRec(arr,mid+1,right);
	    //把左右两边部分有序的数组合并
	    //System.out.println("索引为"+left+"-"+mid+"的左半数组 和 索引为"+(mid+1)+"-"+right+"的右半数组已经局部有序,将它们合并,使"+left+"-"+right+"整体有序");
	    merge(arr,left,mid,right);
	    //AlgorithmComparatorUtil.printIntArr(arr);//自己写的测试工具
	    System.out.println();
	
	}
	
	/**
	 * 将已经有序的左右两个数组合并
	 * @param arr 要合并的数组
	 * @param left 左半边数组的开始位置
	 * @param mid mid为左半边数组的结束位置,mid+1为右半边数组的开始位置
	 * @param right 右半边数组的结束位置
	 */
	public static void merge(int[] arr,int left,int mid,int right){
	    //左数组的索引指针
	    int leftIndex = left;
	    //右数组的索引指针
	    int rightIndex = mid+1;
	
	    //用来存放数据的临时数组
	    int[] tmpArr = new int[right-left+1];
	    //临时数组的索引指针
	    int tmpIndex = 0;
	
	    //比较左边跟右边数组的元素,把更小的元素放入临时数组,有一边完成排序时(索引越界时)结束
	    //  为了保证稳定性,相同的元素则左边优先
	    while(leftIndex<=mid && rightIndex<=right){
	        tmpArr[tmpIndex++] = arr[leftIndex]<=arr[rightIndex] ? arr[leftIndex++] : arr[rightIndex++];
	    }
	
	    //有一边排完了全部元素(即索引越界),把另一边的元素也全部排进去
	    while(leftIndex<=mid){
	        tmpArr[tmpIndex++] = arr[leftIndex++];
	    }
	    while(rightIndex<=right){
	        tmpArr[tmpIndex++] = arr[rightIndex++];
	    }
	
	    //把排好的临时数组覆盖原数组
	    for(int i=left;i<=right;i++){
	        arr[i] = tmpArr[i-left];
	    }
	
	}

迭代方式

	/**
     * 用迭代的方式实现归并排序
     * @param arr
     */
    public static void mergeSort(int arr[]){
        if(arr==null || arr.length<2) return;

        //子数组的长度为1时自然有序,所以从1开始归并,每次归并 子数组长度*2
        for(int subArrLen=1;subArrLen<arr.length;subArrLen*=2){
            //需要合并数组的长度为subArrLen,左边界为left,右边界为right
            int left = 0;
            while(left < arr.length){
                //给right赋值,为了避免越界,还要比较left+subArrLen*2-1和arr.length-1
                int right = Math.min(left+subArrLen*2-1 , arr.length-1);
                if(right-left+1 > subArrLen){//区间大于子数组的长度才合并,否则没意义
                    merge(arr,left,left+subArrLen-1,right);
                }
                left += subArrLen*2;
            }

        }
    }

	/**
     * 将已经有序的左右两个数组合并
     * @param arr 要合并的数组
     * @param left 左半边数组的开始位置
     * @param mid mid为左半边数组的结束位置,mid+1为右半边数组的开始位置
     * @param right 右半边数组的结束位置
     */
    public static void merge(int[] arr,int left,int mid,int right){
        //左数组的索引指针
        int leftIndex = left;
        //右数组的索引指针
        int rightIndex = mid+1;

        //用来存放数据的临时数组
        int[] tmpArr = new int[right-left+1];
        //临时数组的索引指针
        int tmpIndex = 0;

        //比较左边跟右边数组的元素,把更小的元素放入临时数组,有一边完成排序时(索引越界时)结束
        //  为了保证稳定性,相同的元素则左边优先
        while(leftIndex<=mid && rightIndex<=right){
            tmpArr[tmpIndex++] = arr[leftIndex]<=arr[rightIndex] ? arr[leftIndex++] : arr[rightIndex++];
        }

        //有一边排完了全部元素(即索引越界),把另一边的元素也全部排进去
        while(leftIndex<=mid){
            tmpArr[tmpIndex++] = arr[leftIndex++];
        }
        while(rightIndex<=right){
            tmpArr[tmpIndex++] = arr[rightIndex++];
        }

        //把排好的临时数组覆盖原数组
        for(int i=left;i<=right;i++){
            arr[i] = tmpArr[i-left];
        }

    }

复杂度分析

时间复杂度

对于长度为n的数组,排序过程中,占有时间的地方有:将数组合并的过程,和辅助数组覆盖原数组的过程。
把长度为2k的子数组进一步分为两个长度为k的子数组。
对于长度为k的子数组进行合并和覆盖时:合并占用时间约等于k,覆盖占用时间为k
故总时间复杂度为:(n + n) + 2*(n/2 + n/2) + 4*(n/4 + n/4) + … + n*(1+1) ≈ O(n * log n)

空间复杂度

排序过程中用到的最大辅助空间为n,每次归并的过程都会申请对应长度的辅助空间,归并完成后释放。故整个排序过程的空间复杂度为:O(n)

稳定性

由上面代码实现可以看出,排序过程不会改变相同元素的相对顺序,故归并排序是稳定的排序算法。

标签:归并,right,int,arr,详解,数组,排序,left
From: https://blog.csdn.net/weixin_51372196/article/details/140350053

相关文章

  • vue中的插槽详解
    插槽(slot)插槽在vue中是一种很常见的写法,让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式一共有三种分类:默认插槽、具名插槽、作用域插槽,下面一一根据案例改造说明1基本案例首先编写一个基本的案例,三个组件展示不同的数据类型 页面进行展示 现在要......
  • 《成绩排序》
    描述给出班里某门课程的成绩单,请你按成绩从高到低对成绩单排序输出,如果有相同分数则名字字典序小的在前。输入描述第一行为n (0<n<20),表示班里的学生数目;接下来的n行,每行为每个学生的名字和他的成绩,中间用单个空格隔开。名字只包含字母且长度不超过20,成绩为一个不大于......
  • 详解C#委托与事件
    在C#中,委托是一种引用类型的数据类型,允许我们封装方法的引用。通过使用委托,我们可以将方法作为参数传递给其他方法,或者将多个方法组合在一起,从而实现更灵活的编程模式。委托类似于函数指针,但提供了类型安全和垃圾回收等现代语言特性。基本概念定义委托定义委托需要指定它所代......
  • 一文详解大语言模型的流行架构与训练技术
    这篇博客全面介绍了大型语言模型(LLMs)的构建流程,从流行架构的选择到实际建模的每个关键步骤。文章首先探讨了LLMs的模型架构,然后详细阐述了数据准备过程,包括数据的收集、清洗和去重,接着是关于如何进行有效标记化的讨论。在模型构建方面,博客详细解释了采用自监督学习方法的预......
  • Memcached介绍和详解
    Memcached介绍和详解Memcached是一个高性能的分布式内存对象缓存系统,通过在内存中缓存数据来减少数据库的读取次数,从而提高动态Web应用程序的速度和效率。下面将详细介绍Memcached的安装、配置和使用方法。Memcached简介Memcached是一个基于内存的缓存系统,它通常用于缓......
  • 阿里云镜像仓库的使用详解
    一、镜像仓库介绍Registry是Docker公司的一项创新,它提供了存放镜像的仓库服务。在构建好镜像后,我们通常会将镜像上传到Registry服务器上进行保存。这样可以保证不会因本机故障而导致镜像丢失,同时,其他机器也能很方便地通过网络方式下载。DockerHub即为Docker官方的Registry服务......
  • 排序算法
    二分查找:在已排序数组A中,定义左边界l和右边界r,获取中间索引m=floor(l+r)/2,然后将中间索引的值a[m]与待搜索值进行比较,相等则找到,返回中间索引,a[m]>t,右侧全都大于t,m-1设置为右边界重新查找,a[m]<t,m+1设为左边界重新查找。一般奇数二分取中间,偶数二分取中间靠左。一般而言,对于包n含个......
  • ES6 Reflect 详解(三)
    Reflect对象与Proxy对象一样,也是ES6为了操作对象而提供的新API。Reflect对象的设计目的有4个。将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只......
  • SQL优化详解
    对于互联网公司来说,随着用户量和数据量的不断增加,慢查询是无法避免的问题。一般情况下如果出现慢查询,意味着接口响应慢、接口超时等问题。如果是高并发的场景,可能会出现数据库连接被占满的情况,直接导致服务不可用。慢查询的确会导致很多问题,我们要如何优化慢查询呢?主要解决办......
  • windows系统服务配置详解,以及开发好的windows服务怎么部署上去
    一、配置服务1、WIN+R打开运行窗口,输入cmd2、输入sccreateServerNamebinpath="E:\myTest.exe"等号后有空格sccreateServerNamebinpath="E:\myTest.exe"如此这般,就讲ServerName加入到了服务当中3、启动服务scstartServerNamescstartServerName4、WIN+R......