算法复习周------递归之“合并排序”

算法的考试周来到了,从今天开始我就开始了算法的复习准备。。。我准备从递归开始依次是动态规划、贪心、回溯。这几章的内容其实挺乱的,但是总结下来会有所收获。也希望能给看blog的人一些收获。付出总有收获的嘛。。。好了闲话少说,这里就开始第一个算法

 

算法:递归——合并排序


算法描述:
作为一个排序算法,它是将待排列的元素分成大小大致相同的两个子集合,并分别对两个子集合排序,然后将排好的子集合合并。相当于把大问题分成若干个小问题,分别处理后然后整合起来。

这里放一个图便于理解:。在这个图中,我们可以看到我们的例子是:8 4 5 7 1 3 6 2 如果我们要对这个数进行排列的话,我们就要分两个步骤去解决这个问题。①将大问题分成很多小问题   ②将小问题处理后合并成大问题

在上述例子中,我们将问题分为了84  57  13  62 这四组,然后对这四组里面的数字排序  Ⅰ:48  57  13   26  ——>  Ⅱ:将最小的问题合并成4578    1326 两个问题,然后合并之后在内部排序(因为45  78这两组数字是有一定规律的,所以排序过程就不用遍历所有数字排序,这也是为什么用合并排序快速的地方----**后面细讲**),然后将这两组数字内部排序为 4578  1236 ——>  Ⅲ:之后将这个两个不完善的两个子序列在合并、合并时仍排序。最终我们得到最后的答案。

 

在这里我要圈出来合并排序的关键点:①算法处理问题时如何将问题由大分小进行递归  ②小问题处理完成后如何在合并成大问题时将两个子序列排序

 

 

这里我贴出来第一个关键点的代码(如何“分”):

 private static void sort(int[] arr,int left,int right,int []temp){
        
            int mid = (left+right)/2;
            sort(arr,left,mid,temp);//左边归并排序,使得左子序列有序
            sort(arr,mid+1,right,temp);//右边归并排序,使得右子序列有序
            merge(arr,left,mid,right,temp);//将两个有序子数组合并操作
        
    }

在代码里,我们传入arr[]待排序数组,传入left(初始的时候是0),right(初始的时候是数组长度-1),而temp[]是我们用到的中间数组(先不用管它)。

这里取arr数组的中间的数作为支点,然后将大问题从中间分开,依次交给两个部分处理。而小问题中又将问题分成小小问题。。。。(类似于分权制度,都不愿意干活,就都分给下属。。)

 

之后我们来看如何“合”的问题。

这里我放上去一个图片便于清晰理解:

 

在这里我就要接受上面提到的合并中巧妙的思想了:大家根据图片会发现:我在合并小问题的时候建立了两个指针,开始的时候分别指向两个子问题的开始端,例如:1与4比较,1小。所以将1放到temp中的第一个。之后红色块的指针后移动一位,比较2与4,2更小然后把2放到temp中。。。经过了好几次后直到右边的6放到temp中(此时右侧没有数了,所以支教把绿色块全部放进去就好),这就是合并的过程。这样就是为什么代码排序效率更高的原因。

 

这里贴上代码:

private static void merge(int[] arr,int left,int mid,int right,int[] temp){
        int i = left;//左序列指针
        int j = mid+1;//右序列指针
        int t = 0;//临时数组指针
        while (i<=mid && j<=right){
            if(arr[i]<=arr[j]){
                temp[t++] = arr[i++];
            }else {
                temp[t++] = arr[j++];
            }
        }
        while(i<=mid){//将左边剩余元素填充进temp中
            temp[t++] = arr[i++];
        }
        while(j<=right){//将右序列剩余元素填充进temp中
            temp[t++] = arr[j++];
        }
        t = 0;
        //将temp中的元素全部拷贝到原数组中
        while(left <= right){
            arr[left++] = temp[t++];
        }
    }
}

PS:最后将temp临时数组中的数放到arr中。

 

时间复杂度:  在这个问题中,时间复杂度可以递推出来:

 

由此我们知道,合并排序的时间复杂度是 O(nlogn。而且,归并排序的最好,最坏,平均时间复杂度均为O(nlogn)。

 

 

(这里面很多图片来自老师的讲义和链接 https://www.cnblogs.com/chengxiao/p/6194356.html),大家也可以去看连接中所讲的,这里我没有写代码,所以需要代码的同学可以so一下。

——————————————————————————————Made By  Pinging

posted @ 2017-11-21 16:02  Ping_ing  阅读(1211)  评论(0编辑  收藏  举报