3-3 归并排序法的优化

来源:互联网 发布:ubuntu pxe boot 编辑:程序博客网 时间:2024/06/05 06:38

3-3 归并排序法的优化

本节介绍上一节实现的归并排序的两个优化:

1、在 mergeOfTwoSortArray 对两个有序的数组进行归并的时候,如果两个数组合并之前就是有序的数组,就不用再复制来复制去的了;

2、在归并的子过程中,如果待排序的数组元素个数很少的情况下,可以使用插入排序,因为插入排序对于近乎有序的数组而言,可以提前终止循环,从而提高整体排序的效率

下面我们具体来说明:

1、归并排序写好以后,我们可以尝试将其与插入排序进行一个 5000 级别的数据排序的比较测试。

结论是显而易见的,因为插入排序的时间复杂度是 O(n^2),归并排序的时间复杂度是 O(nlogn)。所以,在绝大多数情况下,归并排序的性能较好,插入排序的效率较低

2、但是我们还要认识到一点,在待排序的数组在近乎有序的前提下,插入排序可以达到 O(n) 的时间复杂度,效果非常好;

3、归并(merge)排序的优化思路:

(1)事实上,当 arr[mid]<=arr[mid+1] 的时候是不用 merge 的,如图所示:

代码实现:

if (arr[mid] < arr[mid + 1]) {    return;}

(2)递归到底的时候,可以使用插入排序优化,例如在处理 16 个元素的数组时候,不用继续递归,此时使用插入排序提升性能。

“递归到底”这件事情可以由我们自己定义:

原来我们定义的递归到底这件事情是:当数组区间里只有一个元素的时候,这个元素就是有序的,所以递归不能再进行下去了。

现在我们定义的递归到底这件事情是:当数组区间里的元素是有限个的时候,我们改用插入排序法来完成排序。

代码实现:

if(right-left<=15){    // 使用插入排序去排序这部分的数组元素}

代码实现:

/** * 对数组给定的部分使用插入排序 * * @param arr   给定数组 * @param left  左边界,能取到 * @param right 右边界,能取到 */private void insertSort(int[] arr, int left, int right) {    for (int i = left + 1; i <= right; i++) { // 第 1 遍不用插入,所以是总长度减去 1        int temp = arr[i];        int j;        for (j = i - 1; j > left; j--) {            if (arr[j] > temp) { // 后移一位                arr[j + 1] = arr[j];            } else {                break;            }        }        arr[j] = temp;    }}

使用 15 是不是效果最优的呢?这是一个经验值。

下面编写测试用例:

/** * 测试归并排序及其性能优化 */@Testpublic void test07() {    int[] randomArray1 = SortTestHelper.generateRandomArray(1000000, 1, 500000000);    int[] randomArray2 = SortTestHelper.copyFromOldArray(randomArray1);    SortTestHelper.testSortEfficiency(new MergeSort(), randomArray1);    SortTestHelper.testSortEfficiency(new MergeSortOptimize1(), randomArray2);    SortTestHelper.testSorted(randomArray1);    SortTestHelper.testSorted(randomArray2);}

执行结果:

您所使用的排序算法是 => merge sort(归并排序)

排序算法耗时 => 0.181 秒

您所使用的排序算法是 => 归并排序性能优化1

排序算法耗时 => 0.101 秒

给定数组按照升序排序!

给定数组按照升序排序!

原创粉丝点击