排序算法入门(六)——归并排序

67 阅读1分钟

算法简介

归并排序(Merge Sort)比较占用内存,但却是一种效率高且稳定的算法。它是建立在归并操作上的一种有效的排序算法。该算法是采用分治法的一个非常典型的应用,分治法简单说就是分而治之,将一个大问题分解为小问题,将小问题解答后合并为大问题的答案。它将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

算法描述

  1. 把长度为n的输入序列分成两个长度为n/2的子序列;
  2. 对这两个子序列分别采用归并排序;
  3. 将两个排序好的子序列合并成一个最终的排序序列。

动图演示

849589-20171015230557043-37375010.gif

代码实现

public class MergeSort {
    // 归并排序
    public static int[] mergeSort(int[] arr, int left, int right) {
        // 如果 left == right,表示数组只有一个元素,则不用递归排序
        if (left < right) {
            // 把大的数组分隔成两个数组
            int mid = (left + right) / 2;
            // 对左半部分进行排序
            arr = sort(arr, left, mid);
            // 对右半部分进行排序
            arr = sort(arr, mid + 1, right);
            //进行合并
            merge(arr, left, mid, right);
        }
        return arr;
    }

    // 合并函数,把两个有序的数组合并起来
    // arr[left..mif]表示一个数组,arr[mid+1 .. right]表示一个数组
    private static void merge(int[] arr, int left, int mid, int right) {
        //先用一个临时数组把他们合并汇总起来
        int[] a = new int[right - left + 1];
        int i = left;
        int j = mid + 1;
        int k = 0;
        while (i <= mid && j <= right) {
            if (arr[i] < arr[j]) {
                a[k++] = arr[i++];
            } else {
                a[k++] = arr[j++];
            }
        }
        while(i <= mid) a[k++] = arr[i++];
        while(j <= right) a[k++] = arr[j++];
        // 把临时数组复制到原数组
        for (i = 0; i < k; i++) {
            arr[left++] = a[i];
        }
    }
}

复杂度分析

若数列中有n个元素,则其中 T(n)=2T(n/2)+nT(n) = 2*T(n/2) + n,其中n就是两个子区间合并的时间复杂度。
经过进一步推导,可以得到 T(n)=2kT(n/2k)+knT(n)=2^k * T(n/2^k) + k * n
我们假设 T(1)=T(n/2k)T(1)=T(n/2^k),也就是说当 n/2kn/2^k 个元素的进行归并排序,达到递归终止条件时:n/2k=1n/2^k=1,即:k=lognk=logn
T(n)=Cn+nlog2nT(n)=Cn+nlog2n

因此时间复杂度为 O(nlogn)O(nlogn)