(V) Data structure - merge sort

Merge sort

Merge sort is based on divide-and-conquer sorting technology. Time complexity in the worst case is O (nlogn), it is one of the most respected algorithms. First merge sort the array into equal halves, and then combining them sorted manner.

main idea

To understand merge sort, we use unsorted array, as follows

We know that merge sort first half of the entire array iteratively divided equally, unless atomic values. Here we see an array of eight project components are classified into two array of size 4.

This does not change the order of items that appear in the original. Now these two arrays is divided into two halves.

We further divide these arrays, and can no longer obtain atomic value divided

Now, we will be exactly the same way when they break down. Please note that the color code to provide these lists.

We first compare the elements of each list, and then sort of way to combine them to another list. We see 14 and 33 in the ranking position. We compared 27 and 10, in the target list of two values, we place the first 10, then 27. We change the order of 19 and 35, and 42 and 44 to place the order.

In the next iteration of the consolidation phase, the list we compare two data values, and then merge them into a list of data values ​​found, all data is placed in sorted order.

After the final combined list should be as follows:

Code development

Realization of ideas

Merge sort will continue to list is divided into equal half, until no longer be divided up. By definition, if it is only one element in the list it will be sorted. Then, merge sort will merge smaller sort the list, as well as the new list is sorted.

Step 1−如果列表中的元素已被排序,则返回。
Step 2−将列表递归分为两半,直到无法再将其划分为止。
Step 3−将较小的列表按排序顺序合并到新列表中。

Fake code

package com.paal.demo.c01SortingBasic;

import com.paal.demo.Sort;

import java.util.Arrays;

/**
 * <p/>
 * <li>title: 基础排序-归并排序</li>
 * <li>@author: li.pan</li>
 * <li>Date: 2019/12/7 12:15 下午</li>
 * <li>Version: V1.0</li>
 * <li>Description: </li>
 */
public class MergeSort implements Sort {


    @Override
    public void sort(Integer[] arr) {
        int n = arr.length;
        sort(arr, 0, n - 1);
    }

    //递归使用归并排序,对arr[l....r]的范围进行进行排序
    private static void sort(Integer[] arr, int l, int r) {

        if (l >= r) //当子序列中只有一个元素递归到底的情况
            return;

        int mid = (l + r) / 2;
        sort(arr, l, mid);
        sort(arr, mid + 1, r);
        merge(arr, l, mid, r);

    }

    //将arr[l...mid]和arr[mid+1...r]两部分进行归并
    private static void merge(Integer[] arr, int l, int mid, int r) {

        // 开辟临时空间,合并左半部分已经排好序的数组和右半部分已经排好序的数组
        Integer[] aux = Arrays.copyOfRange(arr, l, r + 1);

        // 初始化,i指向左半部分的起始位置索引;j指向右半部分起始索引位置mid+1
        int i = l, j = mid + 1;
        for (int k = l; k <= r; k++) {  // k指向两个元素比较后归并下一个需要放置的位置

            /**
             * 考虑数组越界
             */
            if (i > mid) {  // 如果左半部分元素已经全部处理完毕,
                arr[k] = aux[j - l];
                j++;
            } else if (j > r) {   // 如果右半部分元素已经全部处理完毕
                arr[k] = aux[i - l];
                i++; // l表示偏移
            }
            /**
             * 真正比较
             */
            else if (aux[i - l] < aux[j - l]) {  // 左半部分所指元素 < 右半部分所指元素
                arr[k] = aux[i - l];
                i++;
            } else {  // 左半部分所指元素 >= 右半部分所指元素
                arr[k] = aux[j - l];
                j++;
            }
        }
    }

}

 /**
     * 希尔排序
     */
    @Test
    public void mergeSortTest() {
        Integer[] integers0 = SortTestHelper.generateRandomArray(100, 0, 1000000);
        Integer[] integers1 = SortTestHelper.generateRandomArray(10000, 0, 1000000);
        Integer[] integers2 = SortTestHelper.generateRandomArray(100000, 0, 1000000);
        System.out.println("------------------------------随机数组--------------------------------");
        System.out.println("插入排序测试1数据量为100"+SortTestHelper.testSort(integers0, new InsertionSort()));
        System.out.println("插入排序测试2数据量为10000"+SortTestHelper.testSort(integers1, new InsertionSort()));
        System.out.println("插入排序测试3数据量为100000"+SortTestHelper.testSort(integers2, new InsertionSort()));
        System.out.println("冒泡排序测试1数据量为100"+SortTestHelper.testSort(integers0, new BubbleSort()));
        System.out.println("冒泡排序测试2数据量为10000"+SortTestHelper.testSort(integers1, new BubbleSort()));
        System.out.println("冒泡排序测试3数据量为100000"+SortTestHelper.testSort(integers2, new BubbleSort()));
        System.out.println("希尔排序测试1数据量为100"+SortTestHelper.testSort(integers0, new ShellSort()));
        System.out.println("希尔排序测试2数据量为10000"+SortTestHelper.testSort(integers1, new ShellSort()));
        System.out.println("希尔排序测试3数据量为100000"+SortTestHelper.testSort(integers2, new ShellSort()));
        System.out.println("归并排序测试1数据量为100"+SortTestHelper.testSort(integers0, new MergeSort()));
        System.out.println("归并排序测试2数据量为10000"+SortTestHelper.testSort(integers1, new MergeSort()));
        System.out.println("归并排序测试3数据量为100000"+SortTestHelper.testSort(integers2, new MergeSort()));
    }

Results of the

------------------------------随机数组--------------------------------
插入排序测试1数据量为100排序时长为: 0.001s
插入排序测试2数据量为10000排序时长为: 0.084s
插入排序测试3数据量为100000排序时长为: 5.868s
冒泡排序测试1数据量为100排序时长为: 0.0s
冒泡排序测试2数据量为10000排序时长为: 0.061s
冒泡排序测试3数据量为100000排序时长为: 9.069s
希尔排序测试1数据量为100排序时长为: 0.0s
希尔排序测试2数据量为10000排序时长为: 0.004s
希尔排序测试3数据量为100000排序时长为: 0.008s

Code optimization

  • Although merge sort algorithm is nlogn level, but in a smaller array of data than when the insertion sort of efficiency is still higher than merge sort, it is possible to break down after the array is small enough to use insertion sort, and then recursively merge sort.

  • If an array is almost ordered, or say that it is ordered, the above steps will be a lot of useless merge operation, it is possible to merge before making a judgment increased efficiency will also be improved to some extent.

Fake code

package com.paal.demo.c01SortingBasic.optimize;

import com.paal.demo.Sort;
import com.paal.demo.c01SortingBasic.InsertionSort;

import java.util.Arrays;

/**
 * <p/>
 * <li>title: 归并排序优化</li>
 * <li>@author: li.pan</li>
 * <li>Date: 2019/12/7 12:15 下午</li>
 * <li>Version: V1.0</li>
 * <li>Description:
 * 优化1: 对于arr[mid] <= arr[mid+1]的情况,不进行merge
 * 优化2: 对于小规模数组, 使用插入排序
 * </li>
 */
public class MergeSortOptimize implements Sort {


    @Override
    public void sort(Integer[] arr) {
        int n = arr.length;
        sort(arr, 0, n - 1);
    }

    //递归使用归并排序,对arr[l....r]的范围进行进行排序
    private static void sort(Integer[] arr, int l, int r) {

        /**
         *  优化2: 对于小规模数组, 使用插入排序
         */
        if( r - l <= 15 ){
            insertionSort(arr, l, r);
            return;
        }

        int mid = (l + r) / 2;
        sort(arr, l, mid);
        sort(arr, mid + 1, r);

        /**
         * 优化1: 对于arr[mid] <= arr[mid+1]的情况,不进行merge
         * 对于近乎有序的数组非常有效,但是对于一般情况,有一定的性能损失
         */
        if (arr[mid] > arr[mid + 1])
            merge(arr, l, mid, r);

    }

    //将arr[l...mid]和arr[mid+1...r]两部分进行归并
    private static void merge(Integer[] arr, int l, int mid, int r) {

        // 开辟临时空间,合并左半部分已经排好序的数组和右半部分已经排好序的数组
        Integer[] aux = Arrays.copyOfRange(arr, l, r + 1);

        // 初始化,i指向左半部分的起始位置索引;j指向右半部分起始索引位置mid+1
        int i = l, j = mid + 1;
        for (int k = l; k <= r; k++) {  // k指向两个元素比较后归并下一个需要放置的位置

            /**
             * 考虑数组越界
             */
            if (i > mid) {  // 如果左半部分元素已经全部处理完毕,
                arr[k] = aux[j - l];
                j++;
            } else if (j > r) {   // 如果右半部分元素已经全部处理完毕
                arr[k] = aux[i - l];
                i++; // l表示偏移
            }
            /**
             * 真正比较
             */
            else if (aux[i - l] < aux[j - l]) {  // 左半部分所指元素 < 右半部分所指元素
                arr[k] = aux[i - l];
                i++;
            } else {  // 左半部分所指元素 >= 右半部分所指元素
                arr[k] = aux[j - l];
                j++;
            }
        }
    }

    // 对arr[l...r]的区间使用InsertionSort排序
    public static void insertionSort(Integer[] arr, int l, int r){

        for( int i = l + 1 ; i <= r ; i ++ ){
            Integer e = arr[i];
            int j = i;
            for( ; j > l && arr[j-1]>e ; j--)
                arr[j] = arr[j-1];
            arr[j] = e;
        }
    }

}

/**
     * 归并排序优化测试
     */
    @Test
    public void mergeOptimizeSortTest() {
        Integer[] integers0 = SortTestHelper.generateRandomArray(100, 0, 1000000);
        Integer[] integers1 = SortTestHelper.generateRandomArray(10000, 0, 1000000);
        Integer[] integers2 = SortTestHelper.generateRandomArray(100000, 0, 1000000);
        System.out.println("------------------------------随机数组--------------------------------");
        System.out.println("插入排序测试1数据量为100" + SortTestHelper.testSort(integers0, new InsertionSort()));
        System.out.println("插入排序测试2数据量为10000" + SortTestHelper.testSort(integers1, new InsertionSort()));
        System.out.println("插入排序测试3数据量为100000" + SortTestHelper.testSort(integers2, new InsertionSort()));
        System.out.println("归并排序测试1数据量为100" + SortTestHelper.testSort(integers0, new MergeSort()));
        System.out.println("归并排序测试2数据量为10000" + SortTestHelper.testSort(integers1, new MergeSort()));
        System.out.println("归并排序测试3数据量为100000" + SortTestHelper.testSort(integers2, new MergeSort()));
        System.out.println("归并排序优化测试1数据量为100" + SortTestHelper.testSort(integers0, new MergeSortOptimize()));
        System.out.println("归并排序优化测试2数据量为10000" + SortTestHelper.testSort(integers1, new MergeSortOptimize()));
        System.out.println("归并排序优化测试3数据量为100000" + SortTestHelper.testSort(integers2, new MergeSortOptimize()));

        Integer[] integers00 = SortTestHelper.generateNearlyOrderedArray(100, 50);
        Integer[] integers11 = SortTestHelper.generateNearlyOrderedArray(10000, 5000);
        Integer[] integers22 = SortTestHelper.generateNearlyOrderedArray(100000, 50000);
        System.out.println("------------------------------近乎有序数组--------------------------------");
        System.out.println("插入排序测试1数据量为100" + SortTestHelper.testSort(integers00, new InsertionSort()));
        System.out.println("插入排序测试2数据量为10000" + SortTestHelper.testSort(integers11, new InsertionSort()));
        System.out.println("插入排序测试3数据量为100000" + SortTestHelper.testSort(integers22, new InsertionSort()));
        System.out.println("归并排序测试1数据量为100" + SortTestHelper.testSort(integers00, new MergeSort()));
        System.out.println("归并排序测试2数据量为10000" + SortTestHelper.testSort(integers11, new MergeSort()));
        System.out.println("归并排序测试3数据量为100000" + SortTestHelper.testSort(integers22, new MergeSort()));
        System.out.println("归并排序优化测试1数据量为100" + SortTestHelper.testSort(integers00, new MergeSortOptimize()));
        System.out.println("归并排序优化测试2数据量为10000" + SortTestHelper.testSort(integers11, new MergeSortOptimize()));
        System.out.println("归并排序优化测试3数据量为100000" + SortTestHelper.testSort(integers22, new MergeSortOptimize()));
    }

Results of the

------------------------------随机数组--------------------------------
插入排序测试1数据量为100排序时长为: 0.001s
插入排序测试2数据量为10000排序时长为: 0.12s
插入排序测试3数据量为100000排序时长为: 8.578s
归并排序测试1数据量为100排序时长为: 0.0s
归并排序测试2数据量为10000排序时长为: 0.013s
归并排序测试3数据量为100000排序时长为: 0.025s
归并排序优化测试1数据量为100排序时长为: 0.0s
归并排序优化测试2数据量为10000排序时长为: 0.001s
归并排序优化测试3数据量为100000排序时长为: 0.003s
------------------------------近乎有序数组--------------------------------
插入排序测试1数据量为100排序时长为: 0.0s
插入排序测试2数据量为10000排序时长为: 0.039s
插入排序测试3数据量为100000排序时长为: 2.764s
归并排序测试1数据量为100排序时长为: 0.0s
归并排序测试2数据量为10000排序时长为: 0.0s
归并排序测试3数据量为100000排序时长为: 0.009s
归并排序优化测试1数据量为100排序时长为: 0.0s
归并排序优化测试2数据量为10000排序时长为: 0.0s
归并排序优化测试3数据量为100000排序时长为: 0.0s

 

Published 87 original articles · won praise 69 · views 130 000 +

Guess you like

Origin blog.csdn.net/lp284558195/article/details/103448354