最近在看"算法"第四版,看到了归并排序时,把这节的练习做了一遍,感觉有些收获,拿出来给大家分享一下,也算抛砖引玉.
将归并排序之前,请大家先考虑一个问题,如何合并和两个有序数组?,比如,这里有两个数组a和b,它们都是有序的
a:2,3,4
b:1,3,5,7
如何合并这两个数组呢? 既然数组有序,我们就充分利用数组的有序性,设置两个指针i,j,分别指向数组的首地址,然后比较指针对应的值a[i]和b[j],每次我们总是取较小值,放入最终数组内.因为两个数组有序,每次都把较小值放入最终数组,那么比完以后最终数组自然有序.
注意,不管两个数组的元素个数和大小如何,在某一时刻,只可能有一个数组元素先用完,不会两个同时用完,为什么?因为根据上面的过程,肯定是数组最大值较小的先用完.即使两个数组元素大小和个数都一样,那么也只会有一个数组先用完.
我们绘制一下这个过程,最开始,i=0,j=0,他们都指向数组的首地址,此时两数较小值是1,所以把b[j]取出放到c中,然后j移到下一个位置.
a:2,3,4
i
b:1,3,5,7
j
c:
a:2,3,4
i
b:1,3,5,7
j
c:1
此时i=0,j=1,继续比较,2<3,2放入c,i移动到下一位值
a:2,3,4
i
b:1,3,5,7
j
c:1,2
此时i=1,j=1,因为此时 a[i]==a[j],所以 a[i]<b[j]不成立,取出b[j]放入c,j移动到下一位值
a:2,3,4
i
b:1,3,5,7
j
c:1,2,3
此时j=2,i=1,继续比较,把3放入c中,i移到下一位值
a:2,3,4
i
b:1,3,5,7
j
c:1,2,3,3
此时,j=2,i=2,继续比较,把4放入c中,i=4,超出a的索引范围,表示a元素已经用完
a:2,3,4
i
b:1,3,5,7
j
c:1,2,3,3,4
因为a元素用完,所以我们只需要依次取出b中剩余元素放入c中,最终得到的c如下:
a:2,3,4
i
b:1,3,5,7
j
c:1,2,3,3,4,5,7
将上面的过程转换成代码
public static int[] merge(int[] a,int[] b){
int len1=a.length;
int len2=b.length;
int len=len1+len2;
int[] c=new int[len];
int i=0,j=0;
for(int k=0;k<len;k++){
//a数组元素用完,依次把b剩余元素放入c中
if(i==len1) c[k]=b[j++];
//b数组元素用完,依次把a剩余元素放入c中
else if(j==len2) c[k]=a[i++];
//如果两个数组都没用完,取较小者放入c中
else if(a[i]<b[j]) c[k]=a[i++];
else c[k]=b[j++];
}
return c;
}
上面的过程很简单,merge方法就是归并.只要把两个有序的数组传给merge,它就会返回一个有序的和数组.那问题来了,如何把一个无序的数组变成两个有序的子数组,以保证merge的正常运行呢?