• 归并排序


    一.定义

          归并排序是将两个排序的子序列合并,形成一个排序的数据序列,又称为两路归并排序.n个元素的数据序列可看成是由n个长度为1的排序子序列组成,反复将相邻的两个子序列归并成一个排序的子序列,直到合并成一个序列,则排序完成.

    二.算法实现

          两路归并排序包括三个方法.核心操作是一次归并,将数组X中相邻的两个排序的子序列归并到数组Y中,成为Y子序列.

          我们要解决问题有三个,一是要归并多少趟才完成,二是一趟里面要归并多少组,三是一组里面如何将两个子序列的数列归并到一个新的数列中

          1.首先,我们解决一个根本问题,如何归并两个子序列到一个新的数列中.

          

    public static void merge(int[] X,int[] Y,int m,int r,int n){//一次归并
    		//归并排序中的核心,归并数组X中的两个子序列到Y数组中
    		//其中x是第一个子序列中的启始下标,而r是第二个子序列的启始下标
    		//n是第二个子序列的长度
    		
    		int i= m,j =r, k = m;
    		
    		while(i < r && j < r+n && j< X.length){
    			if(X[i] < X[j]){ //比较两个子序列中较小的一个,将其放入到数组Y中的下标为k的位置
    				Y[k++] = X[i++];  //放入后,两者的序列各自增加一
    			}else{
    				Y[k++] = X[j++];  //放入后,两者的序列各自增加一
    			}
    		}
    		
    		while(i < r){     //将前一个子序列中剩余元素复制到Y中
    			Y[k++] = X[i++];
    		}
    		
    		while(j < r+n && j<X.length){  //将后一个子序列剩余元素复制到Y中
    			Y[k++] = X[j++];
    		}
    	}
    

      首先,这个两个子序列已经是排序好的了,具有一定的序列性,或是升序或是降序.所以才有了其他的上面的X[i]<X[j]的比较,将两个子序列中比较小的一个放入到数组Y中的下标k中.接着,这两个子序列的长度不一定相同,而且就算相同比较到最后也不一定会将各自的子序列的数据都放入到Y中,所以到了i<r或者j<r+n或者j<X.length其中一个条件不符合的时候,就可以用到while语句中的部分,将前一个子序列中剩余复制到Y中,或者是将后一个子序列剩余元素复制到Y中.随便提出一个问题,为什么在第一个和第三个while循环中,有j<X.length的这个判断呢?j的初始值是第二个子序列的启始下标,随着j的增加都不应该会超过X.length,所以没有必要判断吧???

          2.接着我们要分析下一趟排序中到底要多少组子序列的合并

    public static void mergepass(int[] X,int[] Y,int n){//一趟归并
    		int i =0;
    		while(i < X.length-2*n + 1){
    			merge(X,Y,i,i+n,n);
    			i += 2*n;
    		}
    		
    		if(i+n < X.length)   //再一次归并,不符合2*n大小的两段序列
    			merge(X,Y,i,i+n,n);
    		else
    			for(int j = i ;j <X.length ;j++) //将X剩余元素复制到Y中
    				Y[j] = X[j];
    	}
    

      首先,mergepass中数组X和Y,另外有n是子序列的长度,当然不是所有的子序列的长度都有n,但是最多有一个子序列的长度不足n.

          另外,并不是n增长到最后i<X.length-2*n+1,可能还有两个子序列没有排序,所以就有了下列的if(i+n<X.length)的判断,如果满足i+n<X.length的比较,就再一次归并.所以就有了上面的问题,为什么带merge方法中的要判断j<X.length,因为如果满足j<r+n这个条件的话,数组下标可能已经越界了,因为n是固定的,但是最后一个子序列的长度可能不足n,所以还要加上j<X.length.

          最后,最末尾的剩下序列凑不成两个子序列的数列复制到Y数组中

    3.一个数组要多少趟算法才能算是将数组排序好

         

    public static void mergeSort(int[] X){
    	int[] Y = new int[X.length];
    	int n =1;
    	while(n<X.length){
    	    mergepass(X,Y,n);
    			
                n*=2;
                if(n<X.length){
                	mergepass(Y,X,n);
                	n*=2;
                }
    		}
    	}
    

      上面这个很简单,就是只要满足n<X.length就可以了.但是这里有一个点睛之笔,很吊,就是将经过mergepass方法的Y和X的数组不断的利用,理论上,只要进行了多少趟排序就要多少个新的数组,但是这里不断是使用这两个空间,把X排序到Y中(此时X中数据排序已经没有用了),然后Y经过mergepass排序后把数据再放回到X数组中,不断循环直至排序完成.这样子,这要一个n长度的数组就可以了.

    三.分析

          归并排序的时间复制度在无论什么情况下,时间复杂度都是O(n*log2n),而空间复杂度是O(n),是稳定排序.

  • 相关阅读:
    Two strings CodeForces
    Dasha and Photos CodeForces
    Largest Beautiful Number CodeForces
    Timetable CodeForces
    Financiers Game CodeForces
    AC日记——整理药名 openjudge 1.7 15
    AC日记——大小写字母互换 openjudge 1.7 14
    AC日记——将字符串中的小写字母换成大写字母 openjudge 1.7 13
    AC日记——加密的病历单 openjudge 1.7 12
    AC日记——潜伏着 openjudge 1.7 11
  • 原文地址:https://www.cnblogs.com/kgrdomore/p/4282537.html
Copyright © 2020-2023  润新知