算法的考试周来到了,从今天开始我就开始了算法的复习准备。。。我准备从递归开始依次是动态规划、贪心、回溯。这几章的内容其实挺乱的,但是总结下来会有所收获。也希望能给看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