【jdk8源码】legacyMergeSort算法=插入排序+分治思想+归并优化,其实就这么简单

      jdk1.7之前的排序用的就是归并排序,legacyMergeSort此方法就是1.7为了兼容之前版本的归并排序。

一、简介

      归并排序(Merge Sort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

二、实现方式

1、自上而下的递归(所有递归的方法都可以用迭代重写)

      ① 将序列每相邻两个数字进行归并操作,形成floor(n/2)个序列,排序后每个序列包含两个元素
      ② 将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
      ③ 重复步骤②,直到所有元素排序完毕

2、自下而上的迭代;

      ① 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
      ② 设定两个指针,最初位置分别为两个已经排序序列的起始位置
      ③ 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
      ④ 重复步骤③直到某一指针到达序列尾
      ⑤ 将另一序列剩下的所有元素直接复制到合并序列尾

三、jdk源码

Arrays类

	public static void sort(Object[] a) {
        if (LegacyMergeSort.userRequested)
        	//这是兼容1.6之前旧版本,采用的是冒泡排序和归并排序
            legacyMergeSort(a);
        else
        	//这里跟 TimSort.sort的思想是一样的
            ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
    }

legacyMergeSort算法

	private static void legacyMergeSort(Object[] a) {
		//定义心的数组,作为排序后的对象
        Object[] aux = a.clone();
        mergeSort(aux, a, 0, a.length, 0);
    }
	private static void mergeSort(Object[] src,
                                  Object[] dest,
                                  int low,
                                  int high,
                                  int off) {
        int length = high - low;

        // 数组大小如果小于7,则使用插入排序
        if (length < INSERTIONSORT_THRESHOLD) {
            for (int i=low; i<high; i++)
                for (int j=i; j>low &&
                         ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
                    swap(dest, j, j-1);
            return;
        }

        // 利用分治思想,进行拆分,递归调用插入排序
        int destLow  = low;
        int destHigh = high;
        low  += off;
        high += off;
        int mid = (low + high) >>> 1;
        mergeSort(dest, src, low, mid, -off);
        mergeSort(dest, src, mid, high, -off);

        // 合并相邻两个已排好的部分,若左边最大的  小于  右边最小的,
        // 说明数组已经排好顺序,直接拷贝
        if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {
            System.arraycopy(src, low, dest, destLow, length);
            return;
        }

        // 进行归并排序,
        for(int i = destLow, p = low, q = mid; i < destHigh; i++) {
            if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)
                dest[i] = src[p++];
            else
                dest[i] = src[q++];
        }
    }

总结

      平均时间复杂度:O(nlogn)
      最佳时间复杂度:O(n)
      最差时间复杂度:O(nlogn)
      空间复杂度:O(n)
      排序方式:In-place
      稳定性:稳定

      不管元素在什么情况下都要做这些步骤,所以花销的时间是不变的,所以该算法的最优时间复杂度和最差时间复杂度及平均时间复杂度都是一样的为:O( nlogn ),但是由于做了以下优化,所以在最好的情况下时间复杂度可以达到O(n)

	// 改进归并排序在归并时先判断前段序列的最大值与后段序列最小值的关系再确定是否进行复制比较。
	//如果前段序列的最大值小于等于后段序列最小值,则说明序列可以直接形成一段有序序列不需要再归并,
	//反之则需要。所以在序列本身有序的情况下时间复杂度可以降至O(n)
    if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {
        System.arraycopy(src, low, dest, destLow, length);
        return;
    }

      归并的空间复杂度就是那个临时的数组和递归时压入栈的数据占用的空间:n + logn;所以空间复杂度为: O(n)。

      归并排序算法中,归并最后到底都是相邻元素之间的比较交换,并不会发生相同元素的相对位置发生变化,故是稳定性算法。

猜你喜欢

转载自blog.csdn.net/u010168160/article/details/107611547