归并是将两个有序的序列归并为一个有序序列,在这里,“两个有序序列”是指同一个序列中含有两部分“有序”的子序列,我们要做的就是把这两个有序的子序列“合并”,使其整体成为一个有序的序列。为了做到这一点,我们可以创建一个和原序列大小相同的空间(这里用数组表示),然后通过两个指针同时扫描原序列中的两个有序的子序列,将较小的数据放到我们新创建的数组中,最后得到的是一个有序的序列,然后将这个有序的序列拷贝的原序列中,这样就完成了原序列的归并。归并算法描述如下:
1、merge(array,left,mid,right)//这里array[left,mid],array[mid+1,right]是有序的子序列
2、 创建temArray,空间大小为right-left+1;
3、 定义临时变量i=left,j=mid+1,k=0;
4、 while(i<=mid && j<=right){
5、 if(array[i] < array[j]){
6、 temArray[k++]=array[i++];
7、 }else{
8、 temArray[k++]=array[j++];
9、 }
10、 }
11、 while(i<=mid){ temArray[k++]=array[i++]; }
12、 while(j<=right){ temArray[k++]=array[j++]; }
13、 for(i=0;i<k;i++){ array[left+i]=temArray[i]; }//将归并好的序列赋给原序列
那么,我们怎么划分"有序子序列"呢?鉴于递归的简洁性,我们可以用递归方法划分:对原序列进行二分,一直进行下去,直到得到的子序列是单个值的时候,单个值的子序列默认是有序的,然后再进行反向归并,如下图所示:
利用递归方法进行归并排序的完整程序如下:
public class Test{ public static void main(String[] args)throws InterruptedException{ int arr[]={1,4,23,0,4,5,34,23,4354,23,12,13}; printArray(arr); merge_sort(arr,0,arr.length-1); printArray(arr); } static void merge_sort(int[] array,int left,int right){ if(left<right){ int mid=left+(right-left)/2; merge_sort(array,left,mid); merge_sort(array,mid+1,right); merge(array,left,mid,right); } } static void merge(int[] array,int left,int mid,int right){ int[] temp=new int[right-left+1]; int i=left,j=mid+1,k=0;//注意,这里的左右两边的子序列的下脚标与上面的调用一定要一致!尤其是右边子序列下脚标从哪开始从哪结束 while(i<=mid && j<=right){ //i=left to mid-1;j=mid to right if(array[i]<array[j]){ temp[k++]=array[i++]; }else{ temp[k++]=array[j++]; } } while(i<=mid){ temp[k++]=array[i++]; } while(j<=right){//j=mid to right temp[k++]=array[j++]; } for(i=0;i<k;i++){ array[left+i]=temp[i]; } } static void printArray(int[] array){ for(int val:array){ System.out.print(val+" "); } System.out.println(); } }
归并排序算法性质:
1、归并排序算法的稳定性和归并算法有关,如果归并是稳定的,则归并排序也是稳定的。
2、归并排序算法的时间复杂度是O(nlgn),空间复杂度是O(n)。
3、归并排序算法的比较次数和序列的初始排序无关。
以上是个人总结,有误之处,请各位大侠指出