版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_23729557/article/details/62897882
归并排序其实也采用的是一种分而治之的思想。即我们将所需排序的数列首先一分为二,再对每一部分进行归并排序,最后将排序好的两部分合并起来。
我第一次接触归并排序,并没有多么深刻的感受,尤其是不明白为什么他能降低时间复杂度。但是,在这几天的思考中,我似乎有了很大的收获。首先,我们看一看普通的排序算法。最简单的一种是冒泡排序,它的核心思想是将元素两两比较,然后交换位置,这样一趟排序后,便能将最大的元素移至数组尾部。关于这一方面的叙述,具体可以参考http://www.cnblogs.com/kkun/archive/2011/11/23/2260280.html,但是我们注意到,每排好一个元素,我们都要进行一趟排序。这样,因为要将所有元素排序,则要排n趟序。时间复杂度易得为O(n^2)。这在很多规模较大的问题中,都显得有点心有余力不足。那么,我们看看本文的主角——归并排序。
首先,我们来看一下对于两个已经有序的数列,如何将他们合并起来:
第一部分 1 3 5 7 第二部分 2 4 6 8
操作步骤:
两个指针分别指向第一个数组的第一个元素和第二个数组的第一个元素,即indexA-->1,indexB-->2
比较indexA和indexB所指向的数值大小,将较小者的数值存入合并后的数组,将指针向后移动一位,直至将所有的元素都存入合并后的数组
在上面的排序过程中,我们可以发现,指针值扫描了一趟,便将数据有序地合并了,这就是为什么归并排序能降低时间复杂度的原因。但是,或许会问,那怎么得到两个有序的子序列呢?其实,这就是要用递归解决了。我们通过递归获得两个有序的子序列,由此进行合并。我们可以参考一下下面的代码:
import java.util.*;
class Main{
public static void main(String[]args){
Scanner in=new Scanner(System.in);
int n=in.nextInt(); //需要排序的个数
int a[]=new int[n];
//接收输入
for(int i=0;i<n;i++){
a[i]=in.nextInt();
}
int b[]=new int[n]; //合并后的临时数组
mergeSort(a,b,0,n-1);
for(int i=0;i<a.length;i++){
System.out.print(a[i]+" ");
}
System.out.println();
}
/**
归并排序
*/
public static void mergeSort(int[]a,int[]b,int left,int right){
if(left<right){
int center=(left+right)/2; //划分数组
mergeSort(a,b,left,center); //递归排序
mergeSort(a,b,center+1,right); //递归排序
merge(a,b,left,center+1,right); //合并数组
}
}
/**
合并数组的函数
*/
public static void merge(int[]a,int[]b,int leftPos,int rightPos,int rightEnd){
int leftEnd=rightPos-1; //左边数组的末端
int index=leftPos; //合并的数组的指针
int num=rightEnd-leftPos+1; //排序的个数
while(leftPos<=leftEnd&&rightPos<=rightEnd){
if(a[leftPos]<a[rightPos]){
b[index++]=a[leftPos++];
}else{
b[index++]=a[rightPos++];
}
}
while(leftPos<=leftEnd){
b[index++]=a[leftPos++];
}
while(rightPos<=rightEnd){
b[index++]=a[rightPos++];
}
//将排序好的数组拷回原数组
for(int i=0;i<num;i++,rightEnd--){
a[rightEnd]=b[rightEnd];
}
}
}
运行效果图: