4.归并排序算法

归并排序

归并排序是将两个排序的子序列合并,形成一个排序数据序列,又称两路归并排序。属于分治法策略。

算法描述:关键字序列 {97,82,75,53,17,61,70,12,61,58,26} 的归并排序(升序)过程如下图,{}表示子序列。将n个元素的数据序列看成由n个长度为1的排序子序列组成,反复降相邻的两个子序列归并成一个排序子序列,直到合并成一个序列,则排序完成。

归并排序算法分析:

  • n个元素归并排序,每趟比较n-1次,数据移动n-1次,进行log2n趟,时间复杂度为O(n*log2n)。
  • 归并排序需要O(n)容量的额外空间,与数据序列的存储容量相等,空间复杂度为O(n)。
  • 归并排序稳定。

归并排序算法实现:

package book;

/**
 * 归并排序:将两个排序的子序列合并,形成一个排序数据序列
 * 写的过程:1、先写一个方法用于排序: 对两个已知的排好序的子序列合并到一个序列中
 *          2、让方法支持 可选 边界
 *          3、进行归并排序,递归调用方法
 */
public class mergeSort {
    public static void main(String[] args) {
        int[] keys1 = {1,2,4,7,3,5,8};
        //merge1(keys1);

        int[] keys2 = {1,2,4,7,3,5,8};
        //merge2(keys2, 0, 4, keys2.length-1);

        int[] keys3 = {97,82,75,53,17,61,70,12,61,58,26};
        mergeSort(keys3, 0, keys3.length-1);
        print(keys3);
    }

    /**
     * 归并排序
     * @param keys 待排序数组
     * @param left 数组左边界
     * @param right 数组右边界
     */
    public static void mergeSort(int[] keys, int left, int right) {
        if (left >= right) return;
        //分成两半
        int mid = left + (right - left)/2;
        //左边排序
        mergeSort(keys, left , mid);
        //右边排序
        mergeSort(keys, mid+1, right);

        //对两个已排好序的子序列 合并到一个序列中
        merge3(keys, left, mid+1, right);
    }

    /**
     * @param keys 待排序数组
     * @param leftPtr 第一个序列的开始位置
     * @param rightPtr 第二个序列的开始位置
     * @param rightBound 数组的右边界
     */
    static void merge3(int[] keys, int leftPtr, int rightPtr, int rightBound){
        int[] newarr = new int[rightBound - leftPtr + 1];
        int mid = rightPtr-1;

        int i = leftPtr;
        int j = rightPtr;
        int k = 0;
        while (i <= mid && j <= rightBound) {
            newarr[k++] = keys[i] <= keys[j] ? keys[i++] : keys[j++];
        }

        //将剩下不用比较的,直接插入到newarr中
        while (i <= mid) newarr[k++] = keys[i++];
        while (j <= rightBound) newarr[k++] = keys[j++];

        //将排好顺序的数组复制回原数组
        for (int l = 0; l < newarr.length; l++) {
            keys[leftPtr + l] = newarr[l];
        }
    }

    //对merge1() 方法进行改造:让其可以选择边界
    static void merge2(int[] keys, int leftPtr, int rightPtr, int rightBound){
        int[] newarr = new int[rightBound - leftPtr + 1];
        int mid = rightPtr-1;

        int i = leftPtr;
        int j = rightPtr;
        int k = 0;
        while (i <= mid && j <= rightBound) {
            //再优化:三元运算符:
            newarr[k++] = keys[i] <= keys[j] ? keys[i++] : keys[j++];
        }
        while (i <= mid) newarr[k++] = keys[i++];
        while (j <= rightBound) newarr[k++] = keys[j++];

        print(newarr);
    }

    //对两个已排好序的子序列合并到一个序列中,例如{1,2,4,7} {3,5,8}
    static void merge1(int[] keys){
        int[] newarr = new int[keys.length];
        int mid = keys.length/2;

        int i = 0;
        int j = mid+1;
        int k = 0;
        while (i <= mid && j < keys.length) {
         //再优化:三元运算符: newarr[k++] = keys[i] <= keys[j] ? keys[i++] : keys[j++];
            if (keys[i] <= keys[j]) {
                newarr[k++]  = keys[i++];
                //等同于下面:
                //newarr[k]  = keys[i];
                //i++;
                //k++;
            } else {
                newarr[k++]  = keys[j++];
            }
        }
        while (i <= mid) newarr[k++] = keys[i++];
        while (j < keys.length) newarr[k++] = keys[j++];

        print(newarr);
    }

    public static void print(int[] keys) {
        for (int key : keys) {
            System.out.print(key+" ");
        }
    }
}

java对象排序

java对象排序要求稳定

对于对象的排序,看源码:

Arrays.java:

 /**
     * Sorts the specified array of objects according to the order induced by
     * the specified comparator.  All elements in the array must be
     * <i>mutually comparable</i> by the specified comparator (that is,
     * {@code c.compare(e1, e2)} must not throw a {@code ClassCastException}
     * for any elements {@code e1} and {@code e2} in the array).
     *
     * <p>This sort is guaranteed to be <i>stable</i>:  equal elements will
     * not be reordered as a result of the sort.
     *
     * <p>Implementation note: This implementation is a stable, adaptive,
     * iterative mergesort that requires far fewer than n lg(n) comparisons
     * when the input array is partially sorted, while offering the
     * performance of a traditional mergesort when the input array is
     * randomly ordered.  If the input array is nearly sorted, the
     * implementation requires approximately n comparisons.  Temporary
     * storage requirements vary from a small constant for nearly sorted
     * input arrays to n/2 object references for randomly ordered input
     * arrays.
     *
     * <p>The implementation takes equal advantage of ascending and
     * descending order in its input array, and can take advantage of
     * ascending and descending order in different parts of the the same
     * input array.  It is well-suited to merging two or more sorted arrays:
     * simply concatenate the arrays and sort the resulting array.
     *
     * <p>The implementation was adapted from Tim Peters's list sort for Python
     * (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
     * TimSort</a>).  It uses techniques from Peter McIlroy's "Optimistic
     * Sorting and Information Theoretic Complexity", in Proceedings of the
     * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
     * January 1993.
     *
     * @param <T> the class of the objects to be sorted
     * @param a the array to be sorted
     * @param c the comparator to determine the order of the array.  A
     *        {@code null} value indicates that the elements'
     *        {@linkplain Comparable natural ordering} should be used.
     * @throws ClassCastException if the array contains elements that are
     *         not <i>mutually comparable</i> using the specified comparator
     * @throws IllegalArgumentException (optional) if the comparator is
     *         found to violate the {@link Comparator} contract
     */
    public static <T> void sort(T[] a, Comparator<? super T> c) {
        if (c == null) {
            sort(a);
        } else {
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, c);
            else
                //改进的归并排序(多路归并排序)
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }
	

TimSort.java:
    /**
     * Sorts the given range, using the given workspace array slice
     * for temp storage when possible. This method is designed to be
     * invoked from public methods (in class Arrays) after performing
     * any necessary array bounds checks and expanding parameters into
     * the required forms.
     *
     * @param a the array to be sorted
     * @param lo the index of the first element, inclusive, to be sorted
     * @param hi the index of the last element, exclusive, to be sorted
     * @param c the comparator to use
     * @param work a workspace array (slice)
     * @param workBase origin of usable space in work array
     * @param workLen usable size of work array
     * @since 1.8
     */
    static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
                         T[] work, int workBase, int workLen) {
        assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;

        int nRemaining  = hi - lo;
        if (nRemaining < 2)
            return;  // Arrays of size 0 and 1 are always sorted

        // If array is small, do a "mini-TimSort" with no merges
        if (nRemaining < MIN_MERGE) {
            int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
            binarySort(a, lo, hi, lo + initRunLen, c);
            return;
        }

        /**
         * March over the array once, left to right, finding natural runs,
         * extending short natural runs to minRun elements, and merging runs
         * to maintain stack invariant.
         */
        TimSort<T> ts = new TimSort<>(a, c, work, workBase, workLen);
        int minRun = minRunLength(nRemaining);
        do {
            // Identify next run
            int runLen = countRunAndMakeAscending(a, lo, hi, c);

            // If run is short, extend to min(minRun, nRemaining)
            if (runLen < minRun) {
                int force = nRemaining <= minRun ? nRemaining : minRun;
                binarySort(a, lo, lo + force, lo + runLen, c);
                runLen = force;
            }

            // Push run onto pending-run stack, and maybe merge
            ts.pushRun(lo, runLen);
            ts.mergeCollapse();

            // Advance to find next run
            lo += runLen;
            nRemaining -= runLen;
        } while (nRemaining != 0);

        // Merge all remaining runs to complete sort
        assert lo == hi;
        ts.mergeForceCollapse();
        assert ts.stackSize == 1;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悬浮海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值