算法 —— 排序 —— 归并排序

“分治”策略

分治,字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。在计算机科学中,分治法就是运用分治思想的一种很重要的算法。分治法是很多高效算法的基础,如排序算法(快速排序,归并排序)等。

这里写图片描述

通俗来说就是把一个大的方案,分解成一个一个小方案,再组织起来。

例如对100个元素排序,可以分解成 2 组50个元素的排序,再分解成 4 组25个元素的排序…最后分解成 50 组 2个元素的排序。最后再对已经有序的元素进行整合。

这里写图片描述



归并

如上所示,归并算法是基于“分治”策略。即:要将一个数组排序,先(递归)将它分成两半分别排序,然后将结果归并起来。

归并过程中,我们需要把两个已经有序(同一数组的前半部分和后半部分)的元素排列,归并成一个有序的元素排列。

这里写图片描述

// "归并" 部分


    public static void main(String[] args) {
        int[] a=new int[]{1,2,8,9,  3,4,5,7};
        aux=new int[a.length];//初始化辅助数组

        merge(a,0,a.length/2-1,a.length-1);
    }

    private static int[] aux;//需要辅助数组,用来作比较

    private static void merge(int[] a, int lo, int mid, int hi) {
        int i = lo;
        int j = mid + 1;

        for (int k = 0; k < a.length; k++) {//对aux[]赋值
            aux[k]=a[k];
        }
        for (int k = lo; k <= hi; k++) {
            if (i > mid) {//左半部分已经有序
                a[k] = aux[j++];
            }
            else if (j > hi) {// 有半部分已经有序
                a[k] = aux[i++];
            }

            else if (aux[i] < aux[j]) {//比较元素,放入数组a[],下标+1
                a[k] = aux[i++];
            } else {
                a[k] = aux[j++];
            }
        }

    }



上面的代码和图文即归并排序中的“归并”部分。将一个无序的数组从 分解 归并 直至成为一个有序的数组,基于归并思想,有两种实现方法,分别是自顶向下的归并排序和自底向上的归并排序。




自顶向下(递归)

递归实现的归并排序是算法设计中分治策略的典型应用。

自顶向下的实现方式和Android View的绘制机制measure()方法很相似,这里穿插一下知识点:

measure过程是后根遍历(DecorView最后setMeasureDiemension()),所以子控件的测量结果影响父控件的测量结果。

这里写图片描述



下面摘选《算法4》中的图片来加深印象。

这里写图片描述

要对子数组a[lo..hi]进行排序,先将它分为a[lo..mid]a[mid+1..hi]两部分,分别通过递归调用将它们单独排序,最后将有序的子数组归并为最终的排序结果。

    public static void main(String[] args) {
        int[] a = new int[] { 1, 2, 8, 9, 3, 4, 5, 7 };
        sort(a);
    }

    private static int[] aux;// 需要辅助数组,用来作比较

    private static void sort(int[] a) {
        aux = new int[a.length];
        sort(a, 0, a.length - 1);
    }

    private static void sort(int[] a, int lo, int hi) {
        if (lo >= hi) {
            return;
        }
        int mid = lo + (hi - lo) / 2;
        sort(a, lo, mid);// 左半边排序,里面可能递归sort和merge方法
        sort(a, mid + 1, hi);// 右半边排序
        merge(a, lo, mid, hi);// 归并,merge方法如上
    }




自底向上(遍历)

  • 自顶向上 : 我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。尽管我们考虑的问题是归并两个大数组,实际上我们归并的数组大多数都非常小。

  • 自底向上 : 实现归并排序的另一种方法是先归并那些微型数组,然后再成对归并得到的数组,如此这般,直到我们将整个数组归并在一次。


1.首先我们把每个元素想象成一个大小为 1 的数组。(两两归并)
2.然后将两个大小为2的数组归并成一个有4个元素的数组。(四四归并)
3.然后再将2个大小为4的数组归并(八八归并),如此下去。

这里写图片描述

    public static void main(String[] args) {
        int[] a = new int[] { 1, 2, 8, 9, 3, 4, 5, 7 };
        sort(a);
    }

    private static void sort(int[] a) {
        int LEN = a.length - 1;
        aux = new int[a.length];

        for (int sz = 1; sz <= LEN; sz *= 2) {// 1,2,4,8,16...

            for (int lo = 0; lo < sz; lo += 2 * sz) {
                //(0,2)(2,4)(4,6)...
                //(0,4)(4,8)(8,12)...
                //(0,8)(8,16)(16,24)...
                merge(a, lo, lo + sz - 1, Math.min(LEN, lo + 2 * sz - 1));
            }
        }
    }

这里写图片描述





归并排序是稳定排序,速度仅次于快速排序,一般用于对总体无序,但是各子项相对有序的数列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值