归并排序的实现

归并排序的思想:分治法;

分治法的思想是,当我们求解一个大问题需要很多的时间,并且方式很难实现,我们发现,如果将这个如果将这个大问题分解成若干个小问题,则问题就变得简单很多,这些小问题求解的同时,原问题也得以求解,我们将一个大问题逐步分解成若干个小问题求解的思想,就是分治法,同时也是递归思想。
归并排序算法就是利用了这种思想。

首先对于一个大的数据集,我们不好排序,我们将这个数据集一分为二,分成两个小的数据集,我们假设,如果这两个数据集排序完成,剩下的工作就是讲这两个有序的数据集合并成一个有序的的数据集,

于是我们的当下任务可以分成两个,首先第一个是,排序这个两个小的数据集,然后的任务是,将这两个小的数据集整合到一个有序的数据集。

解决第一个任务:拆分
如果原来的数据集很大,我们拆分成两个数据集的时候,发现,还是不好排序,怎么办,答案很简单,说明它还不够小,于是,我们对着两个数据集,也采用相同的做法,在拆分,以此类推,一直到我们的拆分的数据集只有一个数据为止,这个时候,我们发现,对于只有一个数据元素的数据集,它的顺序也就已经确定了,我们的排序也就完成了。也就是说只要我们对数据不停的拆分,最后将每一个数据集拆分成只有一个数据元素的时候,其排序过程其实就已经完成了。

解决第二个任务:合并
经过上一个步骤,我们就得到了两个有序的数据集合,然后,我们的任务就是将这两个有序的数据集合并成一个有序的数据集合。这里我们举一个例子,桌子上有两副有序的牌,我们要将他们合并成一副有序的牌,我们该怎么做呢,很简单,假设两副陪得顺序都是从小到大排放,小的在上面,首先,我们分别从两副牌的顶端,各取一个,然后比较谁打谁小,将小的放到手中,大的不动,然后在从两副牌的顶端在取两张牌,再比,以此类推,知道一副牌比完,或者两副牌都同时比完,如果其中的一副牌先比完,则将剩下的牌就不用比了,直接放到手中就行了,这时,手中的牌就是合并好的有序的牌了。
好了原理我们说的差不多了,然后我们来说一下如何用代码实现呢?
通过上面分析,我们发现,第一个任务似乎不是很难,我们先完成拆分。

public static void mergerInside(int[] arr, int start, int end) {
        if (start < end) {
            int middle = (start + end) / 2;
            
            //对左边数据集在拆分
            mergerInside(arr, start, middle);
            
            //对右边数据集在拆分
            mergerInside(arr, middle + 1,end);
            
            //合并左右数据集
            merger(arr, start, middle, end);
        }
    }

通过上面的分析我们知道,这个方法是一个递归的过场,也就是在拆分完成到最后,左右两边的数据集都是已经排好序的,然后我们进行合并,合并的结果也是一个有序的数据集。这样,我们的递推关系就可以正确进行下去了。
其实,拆分并不是最难的部分,我们的关键重点是合并。


    public static void merger(int[] arr, int start, int middle, int end) {
        
        //左边数组的长度
        int left_len = middle - start + 1;
        
        //右边数组的长度
        int right_len = end - middle;
        
        //左边数组
        int[] leftArr = new int[left_len];
        
        //右边数组
        int[] rightArr = new int[right_len];

        int i = 0;
        int j = 0;
        
        //为左边数据赋值
        for (; i < left_len; i++) {
            leftArr[i] = arr[start + i];
        }
        
        //为右边数组赋值
        for (; j < right_len; j++) {
            rightArr[j] = arr[middle + 1 + j];
        }

        i = 0;
        j = 0;
        int k = start;

        //从左右两边的顶端分别取一个数据,然后比较,将小的放到第一个位置
        while (i < left_len && j < right_len) {
            
            if (leftArr[i] <= rightArr[j]) {
            
                arr[k++] = leftArr[i++];
                
            } else {
                arr[k++] = rightArr[j++];
            }
        }

        //右边的数据用完了,然后直接将左边的数据复制到数组中
        if (i < left_len) {
            arr[k++] = leftArr[i++];
        }
        
        //左边数据用完了,然后直接将右边的数组复制到数组中
        if (j < right_len) {
            arr[k++] = rightArr[j++];
        }
    }

优化

public class Demo {
    public static void main(String[] args) {
        int[] arr = { 3, 7, 5, 8, 9, 10, 4, 2, 13, 15 };
        
        //排序数组
        mergerSort(arr);
        
        for (int i : arr) {
            System.out.println(i);
        }

    }

    public static void mergerSort(int[] arr) {
    
        //建立好两个大数组,避免多次创建数组
        int[] leftArr  = new int[arr.length];
        int[] rightArr = new int[arr.length];
        
        
        mergerInside(arr, 0, arr.length - 1,leftArr,rightArr);
    }

    public static void mergerInside(int[] arr, int start, int end,int[] leftArr,int[] rightArr) {
        
        if (start < end) {
            int middle = (start + end) / 2;
            mergerInside(arr, start, middle,leftArr,rightArr);
            mergerInside(arr, middle + 1, end,leftArr,rightArr);
            merger(arr, start, middle, end,leftArr,rightArr);
        }

    }

    public static void merger(int[] arr, int start, int middle, int end,int[] leftArr,int [] rightArr) {

        int left_len = middle - start + 1;
        int right_len = end - middle;

        int i = 0;
        int j = 0;

        for (; i < left_len; i++) {
            leftArr[i] = arr[start + i];
        }
        
        for (; j < right_len; j++) {
            rightArr[j] = arr[middle + 1 + j];
        }

        i = 0;
        
        j = 0;
        
        int k = start;

        while (i < left_len && j < right_len) {
            
            if (leftArr[i] <= rightArr[j]) {
                
                arr[k++] = leftArr[i++];
                
            } else {
                arr[k++] = rightArr[j++];
            }
        }

        if (i < left_len) {
            
            arr[k++] = leftArr[i++];
        }
        if (j < right_len) {
            
            arr[k++] = rightArr[j++];
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值