/*
* 归并排序
* 每次都只是操作两个序列,比如3,2,6,5
* 第一次3与2,合成一个有序序列2,3: 在hebing函数中的low,mid,high分别为(0,1,1)
* 第二次6与5,合成一个有序序列5,6:在hebing函数中的low,mid,high分别为(2,2,3)
* 第三次有序序列2,3与有序序列5,6合并成2,3,5,6:在hebing函数中的low,mid,high分别为(0,1,3)
* 合并的函数在hebing()这个函数里,hebing函数第一个参数数组为原数组
* 第二个参数low是第一个序列的开始下标
* 第三个参数是第一个序列的结束下标,也就是mid+1为第二个序列的开始下标
* 第四个参数是第二个序列的结束下标
*/
public class Main {
/*
* 下面这个函数执行的是把两个有序序列合并成一个有序序列
* 和并2与3,一个元素自然的就有序了,所有归并排序都是分割成每个序列含有一个元素的有序序列,然后再合并,依次类推,1,2,4.。。
* 下面这个函数你自己画一下,很快就知道怎么回事,
*
*/
static void hebing(int a[], int low, int mid, int high){
int i,j,k=0;
i=low;
j=mid+1;
int arr[] = new int[high-low+1];//临时申请两个有序序列的长度数组,存放合并后的有序序列
while(i<=mid && j<=high){
if(a[i]<=a[j]){
arr[k++] = a[i++];
}else{
arr[k++] = a[j++];
}
}
while(i<=mid){
arr[k++] = a[i++];
}
while(j<=high){
arr[k++] = a[j++];
}
//赋值回原数组相应的位置
for(i=low,k=0;i<=high;i++,k++){
a[i]=arr[k];
}
}
public static void main(String[] args) {
int a[] = {2,1,3,4,9,5,7,2,3}; //待排序数组
int low,mid,high,size;
//low为第一个序列的开始下标
//mid为第一个序列的结束下标,mid+1就是第二个序列的开始下标(分割两个序列的)
//high为第二个序列的结束下标
//我们知道两个有序序列的开始和结束下标,是不是就可以很轻松的实现两个有序序列合并成一个有序序列
size=1;
while(size<=a.length){ //当一个序列的长度大于数组长度,退出,原数组就成了一个有序的序列
low = 0;
//当一个序列的开始下标加上这个序列的长度超过或等于原数组长度的时候,这个一系列序列长度为size的循环就结束了
while(low+size<a.length){
mid = low + size - 1;
high = mid + size;
if(high>a.length){
high = a.length-1; //当high超过数组长度,赋值为a.length-1(减1是因为high为下标)
}
hebing(a,low,mid,high);//合并两个有序序列的函数
low = high+1; //为下一个两个序列做准备
}
size*=2;//给size*2,就是1,1+1=2,2+2=4,4+4=8,1,2,4,8就是size的每个阶段值
}
for(int i=0;i<a.length;i++){
System.out.print(a[i]+" ");
}
}
以下是我单步调试的结果,看了这个调试过程中原数组的变化,自己再在纸上画一下,就可以理解了,我刚开始也看了好长时间,自己画了一下,才知道他是怎样执行的,怎样传递参数的。
第一次循环:第一次出了hebing()函数后,原数组下标为0,1的元素序列有序了(上图)。
第一次循环:第二次出了hebing()函数后,原数组下标为2,3的元素序列有序了(上图)。
第一次循环:第三次出了hebing()函数后,原数组下标为4,5的元素序列有序了(上图)。
第一次循环:第四次出了hebing()函数后,原数组下标为6,7的元素序列有序了(上图)。
第一次循环完了,进行第二次循环,size=1*2=2;
第二次循环:第一次出了hebing()函数后,原数组下标为0,1,2,3的元素序列有序了(上图)。
不写了,以此类推。。。。
一定要自己画一下,画一下很容易理解。
归并排序比较稳定,最坏,最好,平均时间复杂度都是O(N*logN)。
归并排序的空间复杂度是O(N),因为要不停的临时存储要合并的子序列。