使用分治法解决问题的基本步骤是:
分解: 将难以直接求解的复杂问题,分解成若干个规模较小、互相独立,且与原问题性质相同的子问题
求解: 将子问题分解至可直接求解
合并: 将子问题的解以某种方式合并成原问题的解
归并排序的核心是:将两个连续的、各自有序的子序列合并成一个有序的子序列。
归并(array, start, mid, end) { // array[start...mid] 有序 // array[mid+1...end] 有序 // 最终使 array[start...end] 有序 i = start; j = mid + 1; cursor = 0; temp_array = 长度是两个子序列长度之和的辅助数组; while (i <= mid && j <= end) { if array[i] <= array[j] temp_array[cursor++] = array[i++]; else temp_array[cursor++] = array[j++]; } 如果第一个子序列没处理到头,则将第一个子序列中剩余的元素复制到 temp_array 的尾部; 如果第二个子序列没处理到头,则将第二个子序列中剩余的元素复制到 temp_array 的尾部; 将 temp_array 复制到 array[start...end]; }
上面的算法是将子问题的解合并成原问题的解的方式,下面是一个分解-归并
的整体过程的示例:
下面的伪代码是归并排序的递归实现:
归并排序(array, start, end) { if (end <= start) 长度为 1 的子序列直接有序,直接返回; // 将子序列一分为 2,然后分别使用归并排序进行排序 mid = (start + end) / 2; 归并排序(array, start, mid); 归并排序(array, mid+1, end); // 然后归并两个有序序列 归并(start, mid, end); }
归并过程的时间复杂度是:n。
根据递归程序的时间复杂度计算公式,可得:
T(n) = 2 * T(n/2) + n ===> T(n) = 2 * (2 * T(n/4) + n/2) + n ===> T(n) = 4 * T(n/4) + 2 * n ===> T(n) = 4 * (2 * T(n/8) + n/4) + 2 * n ===> T(n) = 8 * T(n/8) + 3 * n ===> ... T(n) = (2^m) * T(n/(2^m)) + m * n 且T(1) = 1 也就是当 n / (2^m) -> 1,即 m -> log2n 时, T(n) = n + n * log2n 所以归并排序的时间复杂度是 O(nlogn)
在归并时,用到了辅助数组,所以归并排序不是原地排序,其空间复杂度是 O(n)