归并排序(java)

2020-05-01  本文已影响0人  castlet

假设初始序列有n个数字,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2个长度为2(或1)的有序子序列;再两两归并,如此反复即可。时间复杂度O(nlogn),是稳定的排序算法。非递归实现的性能好于递归的方式。

  8   3   12   5   10
  \  /     \  /    /
 [3 8]   [5 12]  [10]
    \     /       /
   [3 5 8 12]   [10]
       \       /
       [3 5 8 10 12]

代码-递归方式

// 1. 递归算法
void mergeSort(int[] arr) {
    if (arr == null || arr.length <= 0) {
        return;
    }
    int[] tmp = new int[arr.length]; //辅助数组
    mergeSort(arr, tmp, 0, arr.length - 1);
}

void mergeSort(int[] arr, int[] tmp, int start, int end) {
    if (start < end) {
        int middle = start + (end - start) / 2;
        mergeSort(arr, tmp, start, middle);        // 对左边的序列进行归并排序
        mergeSort(arr, tmp, middle + 1, end); //对右边的序列进行归并排序
        merge(arr, tmp, start, middle, end);       // 合并左右两个有序的序列
    }
}

// 将有序的arr[start...middle -1]和arr[middle...end]合并为有序的arr[start...end]
void merge(int[] arr, int[] tmp, int start, int middle, int end){
    if (start >= end) {
        return;
    }
    int i = start;       // 左边序列的索引
    int j = middle + 1;  // 右边序列的索引
    int k = start;       // 合并后有序序列的索引,用tmp来临时保存合并后的有序序列

    while (i <= middle && j <= end) {
        if (arr[i] < arr[j]) {
            tmp[k] = arr[i];
            i++;
        } else {
            tmp[k] = arr[j];
            j++;
        }
        k++;
    }
    if (i <= middle) {
        // 若左边序列还有剩余,则将左边序列剩余数字拷贝到tmp中
        while (i <= middle) {
            tmp[k] = arr[i];
            i++;
            k++;
        }
    } else if (j <= end) {
        // 若右边序列还有剩余,则将右边序列剩余数字拷贝到tmp中
        while (j <= end) {
            tmp[k] = arr[j];
            j++;
            k++;
        }
    }

    // 将tmp里的有序序列拷贝到原始数组中
    for (i = start; i <= end; i++) {
        arr[i] = tmp[i];
    }
}

代码-非递归方式

// 2.非递归算法
void mergeSort2(int[] arr){
    if (arr == null || arr.length <= 0) {
        return;
    }
    int k = 1; // 归并的子序列长度,初始值为1
    int[] tmp = new int[arr.length];
    while (k < arr.length) {
        mergeSort2(arr, tmp, k); // 对长度为k的子序列进行两两归并
        k = k * 2;               // 自序列加倍
    }
}

void mergeSort2(int[] arr, int[] tmp, int k){
    int i = 0;
    while (i + 2 * k <= arr.length) {
        merge(arr, tmp, i, i + k - 1, i + 2 * k - 1); // 分解成两个子序列进行归并
        i = i + 2 * k;
    }

    if (i + k < arr.length) {
        merge(arr, tmp, i, i + k - 1, arr.length - 1); // 归并最后两个子序列
    }
}
上一篇 下一篇

猜你喜欢

热点阅读