归并算法详解

MergeSort归并排序

[1] 归并排序的原理

1.1 将两个有序数组合并成一个有序数组

两个数组,每一个数组内部都是有序的,比如:
a{5,7} 和 b{6,11,37}

我们申请足够大的空间,来放排好序的数组。比如这个数组叫 c{}。

每次取两个数组中最小的数,进行比较,小的取出放入 c{}。
如此将两个数组中的元素都取完,全部放入 c{},c{} 中就是一个有序的数组。比如:

第1次:5和6比较,5较小,所以 a{7}, b{6,11,37} -> c{5}
第2次:7和6比较,6较小,所以 a{7}, b{11,37} -> c{5,6}
第3次:7和11比较,7较小,所以 a{}, b{11,37} -> c{5,6,7}
第4次:a{} 空了,所以 b{} 中剩下的都是排好序的大的,所以 a{}, b{} -> c{5,6,7,11,37}

1.2 递归产生有序数组

每次:把一个无序数组,分成前后两半(根据数组中的元素个数,就可以确定到哪里是一半),首先对前一半进行递归调用,然后对后一半进行递归调用。最后归并,产生一个完整的有序数组。

比如:{7, 5, 37, 6, 11},

一共有5个元素,5/2=2,所以分成 {7,5} {37,6,11}。

   对{7,5} 进行递归调用,一共有2个元素,2/2=1,所以分成 {7} {5}。
   对{7} 进行递归调用,发现到头了,返回。
   对{5} 进行递归调用,发现到头了,返回。
   于是归并 {7} {5},得到有序数组 {5,7},返回。

   对{37,6,11} 进行递归调用,一共有3个元素 3/2=1,所以分成 {37} {6,11}。
   对{37} 进行递归调用,发现到头了,返回。

   对{6,11} 进行递归调用,一共有2个元素 2/2=1,所以分成 {6} {11}。
     对{6} 进行递归调用,发现到头了,返回。
     对{11} 进行递归调用,发现到头勒,返回。
     于是归并 {6} {11},得到有序数组 {6,11},返回。

   于是归并 {37} {6,11},得到有序数组 {6,11,37},返回。

于是归并 {5,7} {6,11,37},得到有序数组 {5,6,7,11,37},返回。排序完成。

[2] 归并排序的实现

package testAlgorithm;

import java.util.Arrays;

/**
 * @author derek zhan
 * @version 
 */
@SuppressWarnings("unchecked")
public class MergeSortTest
{
    //排序中的临时数组
    private Comparable [] tmpArray ;
    
    public static void main(String[] args)
    {
        Integer [] obj = {1,4,7,2,5,8};
        MergeSortTest mst = new MergeSortTest();
        mst.sort(obj);
        System.out.println(Arrays.toString(obj));
        Integer[] obj2 = {8,5,2,7,4,1};
        mst.mergeSort2(obj2);
        System.out.println(Arrays.toString(obj2));
    }
    
    /** 
    * *利用归并排序算法对数组obj进行排序 
    */ 
    public void sort(Comparable[] obj) { 
        tmpArray = new Comparable[obj.length];// 初始化中间数组 
        mergeSort1(obj, 0, obj.length - 1); // 归并排序 
        tmpArray = null; 
    } 
    
    //第一个种方法
    /**
     * 递归划分成2个数组
     * @param obj 要排序的数组
     * @param left 数组中第一个元素下标
     * @param right 数组最后一个元素的下标
     */
    public void mergeSort1(Comparable[] obj,int left,int right){
        if(left < right){//只要不是数组的最后一个元素
            int center = (left+right)/2;
            mergeSort1(obj, left, center); //划分成左数组
            mergeSort1(obj, center+1, right);//划分成右数组
            merge1(obj, left, center, right);//合并2一个数组并排序
        }
    }
    //第二种方法
    public void mergeSort2(Comparable[] obj){
        int n = obj.length;
        if(n<=1) return; //如果只有一个元素则返回.
        Comparable left[] = new Comparable[n/2]; //左数组
        Comparable right[] = new Comparable[n-n/2];  //右数组
        for(int i=0,leftPos=left.length; leftPos<obj.length; i++,leftPos++){
            if(i<left.length){ //给左数组赋值
                left[i] = obj[i];
            }
            //给右数组赋值
            right[i] = obj[leftPos];
            
        }
        mergeSort2(left);
        mergeSort2(right);
        merge2(obj, left, right);
    }
    
    /**
     * 合并数组并排序
     * @param obj 要合并的数组
     * @param left 左边数组第一个元素下标
     * @param center 左边数组最后一个元素下标
     * @param right 右边数组最后一个元素下标
     */
    public void merge1(Comparable[] obj,int left,int center,int right){
        int midd = center+1;
        int temp = left;
        int third = left;
        while(left <= center && midd <= right){
            if(obj[left].compareTo(obj[midd])<=0){ //比较2个数组中的值,把小值放入临时数组
                tmpArray[third++] = obj[left++]; 
            }else{
                tmpArray[third++] = obj[midd++]; 
            }
        }
        //如果第一个数组已经全部比较完了,那么我们只要直接复制第二个数组的条目到合并数组中即可 
        while(midd<=right){
            tmpArray[third++] = obj[midd++]; 
        }
        //如果第二个数组已经全部比较完了,那么我们只要直接复制第一个数组的条目到合并数组中即可 
        while(left <= center){
            tmpArray[third++] = obj[left++]; 
        }
        //拷贝bridge to obj数组
        while(temp<=right){
            obj[temp] = tmpArray[temp];
            temp++;
        }
    }
    
    //第二种合并方法
    public void merge2(Comparable[] obj,Comparable[] left,Comparable[] right){
        int leftLength = left.length-1;
        int rightLength = right.length-1;
        int k=0; 
        int leftPos=0,rightPos=0; // left数组下标, right数组下标 
        while(leftPos<=leftLength||rightPos<=rightLength){ 
            Comparable temp = null; 
            if(leftPos>leftLength){  //如果第一个数组已经全部比较完了,那么我们只要直接复制第二个数组的条目到合并数组中即可 
                temp=right[rightPos++]; 
            }else if(rightPos>rightLength){  //如果第二个数组已经全部比较完了,那么我们只要直接复制第一个数组的条目到合并数组中即可 
                temp=left[leftPos++]; 
            }else if(left[leftPos].compareTo(right[rightPos])>0){  //把比较的两个条目中关键值小的放到合并数组中 
                temp=right[rightPos++]; 
            }else{ 
                temp=left[leftPos++]; 
            } 
            obj[k++]=temp; //把比较完的值放入数组
            } 
        } 
}

  

最后执行结果:

[1, 2, 4, 5, 7, 8]
[1, 2, 4, 5, 7, 8]

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值