归并排序及其运用

44 阅读2分钟

归并排序的原理并不复杂,对于给出的数组arr,其排序的步骤为:

  1. 将arr分成大致相等的左右两部分(过程为递归,直至左边部分大于等于右边部分结束);
  2. 对左边部分进行排序,使其有序;
  3. 对右边部分进行排序,使其有序;
  4. 合并左右两部分,使整体有序。

代码如下:

private static void sort(int[] arr) {

    if (null == arr || arr.length < 2) {
        return;
    }

    process(arr, 0, arr.length - 1);
}

private static void process(int[] arr, int left, int right) {

    if (left >= right) {
        return;
    }

    int mid = left + ((right - left) >> 1);

    process(arr, left, mid);

    process(arr, mid + 1, right);

    merge(arr, left, mid, right);

}


private static void merge(int[] arr, int left, int mid, int right) {

    int pLeft = left;
    int pRight = mid + 1;

    int[] help = new int[right - left + 1];
    int i = 0;


    while ((pLeft <= mid) && (pRight <= right)) {
        help[i++] = arr[pLeft] < arr[pRight] ? arr[pLeft++] : arr[pRight++];
    }

    while (pLeft <= mid) {
        help[i++] = arr[pLeft++];
    }

    while (pRight <= right) {
        help[i++] = arr[pRight++];
    }

    i = left;
    for (int e : help) {
        arr[i++] = e;
    }

}

在合并过程中,由于左边部分和右边部分已经严格有序,因此如果存在诸如数组中比i位置的数小或大多少倍的数总共有多少之类的问题,就可以通过合并过程得出。比如求数组中排在i前面并且比i小的数的累加和,可以通过比较左右两组数每个位置直接得出,核心代码如下:

int res = 0;
while (leftIndex <= mid && rightIndex <= right) {
    res = arr[leftIndex] < arr[rightIndex] ? (right - rightIndex + 1) * arr[leftIndex] : 0;
    ......
}

当左组leftIndex位置比右组rightIndex位置数小,那么右组从rightIndex到right之间的所有数都比其大(因为右组有序),因此通过下标换算可以直接得出共有right - rightIndex + 1个arr[leftIndex]产生,因此累积和为(right - rightIndex + 1) * arr[leftIndex]。而整个过程产生多少小和,无非是左组排序过程中产生的小和+右组排序过程中产生的小和+合并过程产生的小和。