归并(合并)算法

来源:互联网 发布:广数车锥度螺纹编程 编辑:程序博客网 时间:2024/06/07 05:56

归并算法,就是把2个有序的数列,归并为一个有序的数列。依赖于归并操作

举例说明:有2堆有序的扑克牌,都是面朝上放置,牌从上到下,由从小到达排序,这个时候需要把这个2堆牌归并,面朝下放置,最小的牌在最下面。那么比方2堆牌分别为A和B,我们先从A和B的最上面的牌来比较,找出最小的那张,放到旁边,面朝下,然后,这张最小的被取走的时候,那么露出来的那张又成了最上面的牌,这个时候,继续比较最上面的牌,把小的放到旁边那张牌上面,就这样重复不停的最上面的牌,直到A,B所有的牌都被放到旁边的那一摞牌上面,这个时候排序完成,这个新的一摞排就是从上到下,按照从小到大排序的,就实现了把2个有序序列合并的操作。

当然在代码里面,我们可以在每个序列的底部放置一个标志位,称为”哨兵”,避免我们要去检查是不是序列排序完成,当然也可自己去检查,当遇到哨兵的时候,表示这个序列所有的元素都被排列完成,当2个要归并的序列都遇到哨兵,那么程序停止执行,归并操作已经完成。

下面的伪码就是实现方法,A是一个数组,p,q,r是下标,A[p..q]和A[q+1..r]都是有序序列,分别为它们添加一个哨兵,在这里我们把‘&’作为哨兵,创建对应的L和R序列,然后把他们排列成一个新的有序序列A[p…r],

MERGE(A,p,q,r)(这个是来自算法导论,哨兵的引入也是增加了一种思路,避免了去计算判断结束这个标志)

  1. n1 <- q-p+1;  //L里面的元素个数
  2. n2 <- r-q;//R里面的元素个数
  3. 创建序列L[1…n1+1]和R[1…n2+1]
  4. for i  <-  1 to n1
  5.       do L[i]<-A[p+i-1] //创建L序列
  6. for j <- 1 to n2
  7.     do R[j] <- A[q+j];//创建R序列
  8. L[n1+1] <- & //放入哨兵
  9. R[n2+1] <- & //放入哨兵
  10. for k <- p to r
  11.    do if L[i]<=R[j]
  12.          then A[k]=L[i]
  13.                  i=i+1
  14.        else
  15.                A[k]=R[j]
  16.                 j=j+1

具体执行就是上面这样。在实际的代码中,可能我们不需要使用“哨兵”,我们直接根据序列的长度就可以来判断是否OK。下面是我们将要使用的实际执行过程的伪码:

直接有有序的数列L[0…n]和R[0…m],然后并归到一个序列merge,并返回结果。

  1. i  <--0
  2. j  <—0
  3. k < --0
  4. 创建新数列merge[m+n+1]
  5.  for k <- 0 to  n+m+1&&i<n&&<m
  6.    do if L[i]<=R[j]
  7.          then merger[k]=L[i]
  8.                  i=i+1
  9.        else
  10.                merger[k]=R[j]
  11.                 j=j+1
  12. if i<n+1
  13.    for k <k to n+m+1
  14.           merger[k]=L[i]
  15.           i=i+1
  16. if j<m+1
  17.    for k<k to n+m+1
  18.         merger[k]=R[j]
  19.          j=j+1
  20.      
  21. return merger

JAVA代码实现:

 /*     * 参数:L,R有序序列     * 返回:归并后的有序序列     */public static int[] merge(int[] L,int[] R){int i=0,j=0,k=0;;int[] merge=new int[L.length+R.length];//创建一个存放归并好的序列的数组for(k=0;i<L.length&&j<R.length;k++){if(L[i]<R[j]){//如果扫描到的的当前L序列的值小于扫描到的R当前序列的数值,把L当前的数值放入归并序列mergemerge[k]=L[i];i++;       //L序列的扫描位置向后移动一位}else{merge[k]=R[j];//如果扫描到的的当前L序列的值大于扫描到的R当前序列的数值,把R当前的数值放入归并序列merge    j++;  //R序列的扫描位置向后移动一位}}if(i<L.length) //扫描完成,发现L序列没有扫描完成,说明R序列已经全部并归完成,剩下的L序列的数值放入merge while(i<L.length){ merge[k]=L[i]; k++; i++; }if(j<R.length) //扫描完成,发现R序列没有扫描完成,说明L序列已经全部并归完成,剩下的R序列的数值放入merge while(i<L.length){ merge[k]=R[i]; k++; j++;     }return merge;//返回完成归并的序列}