一.归并排序

归并排序


这几天在学算法,打算写成博客记下来加深一下印象,其中有什么理解的不对或者写错的地方请点出。
第一个是归并排序,该算法是分治法的典型应用,完全遵循分治模式,直观的操作如下:
1.分解:分解待排序的n个元素的序列成两个n/2的子序列。
2.解决:使用归并排序递归地排序两个子序列。
3.合并:合并两个已排序的子序列以产生已排序的最终序列。
当分解到序列的长度为1时,递归开始回升,此时长度为1的每个序列都是已经排好序的。
关键操作是合并两个已经排好序的子数组,该步骤通过一个合并方法来完成,以玩扑克牌的例子,
假设桌上有两堆已排序的扑克牌,最小的牌在上面,我们在这两堆牌的最上面那一张中选取较小的牌,
将该牌取出,放到输出堆。重复这个步骤,直到其中一个堆空了,此时把另一堆剩下的牌也放在输出堆,
由此,我们就把这两堆牌合并成有序的一堆牌。该方法有4个参数,分别是一个数组和3个数组的下标,
假设子数组arr[p…q]和arr[q+1…r]已经排好序,其中p<=q

public static void merge(int[] arr, int p, int q, int r)
    {
        int n1 = q - p + 1;
        int n2 = r - q;
        int[] left = new int[n1];
        int[] right = new int[n2];
        for (int i = 0; i < n1; i++){
            left[i] = arr[p + i];
        }
        for (int j = 0; j < n2; j++){
            right[j] = arr[q + j +1];
        }
        int a = 0, b = 0;
        for (int k = p; k <= r; k++){
            if (a < n1 && b < n2){
                if (left[a] <= right[b]){
                    arr[k] = left[a];
                    a++;
                    continue;
                }else{
                    arr[k] = right[b];
                    b++;
                    continue;
                }
            }
            if (a >= n1 && b < n2){
                arr[k] = right[b];
                b++;
                continue;
            }
            if (a < n1 && b >= n2){
                arr[k] = left[a];
                a++;
                continue;
            }
        }
    }

接下来就是第一步分解和第二步解决了,递归排序的代码实现如下:

    public static void megerSort(int[] arr, int p, int r)
    {
        if (p < r){
            int q = (p + r) / 2;
            megerSort(arr, p, q);
            megerSort(arr, q + 1, r);
            merge(arr, p, q, r);
        }
    }

假设排序数组[87,80,8,24,31,12,78,57,83,7]中的元素,9/2后两个子数组分为[87,80,8,24,31],[12,78,57,83,7],
针对左数组[87,80,8,24,31],4/2后两个子数组分为[87,80,8],[24,31],2/2后两个子数组分为[87,80],[8],最后可以得到[87],[80]两个
子数组,此时两个子数组已经有序,而且不满足p < r,进行回升的过程,调用merge方法,得到有序数组[80,87],接着合并[87,80],[8],
得到[8,80,87],合并[24],[31]得到,[24,31],然后合并[8,80,87],[24,31],得到[8,24,31,80,87],右数组的过程也是如此,在这整个归并排序
中,首先排序左数组,当左数组有序,接着排序右数组,然后合并,重复这个过程直到排序完成。
算法分析:
1. 时间复杂度
在归并排序中可以看到,利用分治法的思想,每次都把问题分解为两个原先问题的一半规模的子问题,所以原问题的时间为两个子问题的和,由于它的形式就是一颗二叉树,根据二叉树可以得知它的时间复杂度为O(nlog2n)(以2为底)。
2. 空间复杂度
归并排序不具有空间原址性(任何时候都只需要常数个额外的元素空间存储临时的数据),在合并的时候需要待合并元素数量大小的存储空间。
测试:

public static void main(String[] args) {
        int[] nums = new int[50];
        Random random = new Random();
        for (int i = 0; i < nums.length; i++) {
            nums[i] = random.nextInt(100);
        }
        for (int i = 0; i < 10; i++) {
            System.out.print(nums[i] + " ");
        }
        System.out.println();
        long start = System.currentTimeMillis();
        megerSort(nums, 0, nums.length - 1);
        long end = System.currentTimeMillis();
        System.out.println("运行时间:" + (end - start)+"毫秒");
        for (int i = 0; i < 50; i++) {
            System.out.print(nums[i] + " ");
            if(i % 10 == 0){
                System.out.println();
            }
        }
    }

效果
接下来分别使用10000,100000,500000个随机数进行测试:
测试

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值