什么是归并排序??
归并排序是基于归并的排序。归并,是将两个或两个以上的有序表合成一个有序表。
假设待排序的数组有 n
个元素,将数组看成是 n
个有序的子数组,每个子数组只有一个元素。然后两两合并,得到每个子数组长度为2。然后继续两两合并,直到合并为长度为 n
的数组。
时间复杂度
平均复杂度是 O(nlogn)
,最好复杂度是 O(nlogn)
,最坏复杂度是 O(nlogn)
。
(图片来源于网络)
将原数组划分子数组的过程看成是一棵二叉树,那么数组划分到每个子数组中只有一个元素时的二叉树高度(递归深度)。最后一层是叶子节点,每个叶子节点是一个元素。那么,最后一层的节点个数是 n
(数组长度)。假设二叉树深度是 k
,那么,从 2^(k-1) = n
得到,k
是 logn
。合并的过程是从子数组(其中只有一个元素)开始向上合并
每次排序需要遍历一次数组,时间是 O(n)
。一共需要 logn
趟排序(二叉树的深度,递归深度)。所以时间复杂度是 O(nlogn)
。
空间复杂度
空间复杂度是 O(n)
。
每次排序中都需要一个数组暂时保存排序前的数组或者是排序后的结果数组,数组占用的空间是 n
(数组的长度)。
使用场景
归并排序是稳定的排序。也就是,相等的元素的顺序不会改变。归并排序的速度仅次于快速排序。一般用于总体是无序的,但是子序列是相对有序的。
public void sort(int[] array) {
mergeSort(array, 0, array.length);
}
public void mergeSort(int[] array, int low, int high) {
if (low >= high) return;
int mid = (low + high) / 2; // 将数组一分为二
mergeSort(array, 0, mid); // 对左边一半进行排序
mergeSort(array, mid, high); // 对右边一半进行排序
merge(array, low, mid, high); // 将两个有序子数组合并为一个有序数组
}
// 将数组左右两半合并为一个排好序的数组。左右两个子数组已经是排好序的数组
public void merge(int[] array, int low, int mid, int high) {
// 将数组array复制到一个新数组copy中,将排序完成后的值写入原数组array
int[] copy = Arrays.copyOf(array, array.length); // 复制
int i = low, j = mid, k = low;
// 逐一比较两个子数组,小者排在前面。直至其中一个数组全部写入结果数组中
while (i < mid && j < high) {
if (copy[i] <= copy[j]) array[k++] = copy[i++]; // 相等的元素,相对位置不改变
else array[k++] = copy[j++];
}
// 如果左边的子数组中剩余的数据写入数组中
while (i < low) array[k++] = copy[i++];
// 如果右边的子数组中剩余的数据写入数组中
while (j < high) array[k++] = copy[j++];
}
/**********************************************************************/
// 将两个有序子数组合并为一个有序数组。第一个有序子数组是[low,mid),第二个有序子数组是[mid,high)
public void merge(int[] array, int low, int mid, int high) {
// 将数组array复制到一个新数组copy中,将排序完成后的值写入原数组array
int[] res = new int[array.length]; // 复制
int i = low, j = mid, k = low;
// 逐一比较两个子数组,小者排在前面。直至其中一个数组全部写入结果数组中
while (i < mid && j < high) {
if (array[i] <= array[j]) res[k++] = array[i++];
else res[k++] = array[j++];
}
// 如果左边的子数组中剩余的数据写入数组中
while (i < low) res[k++] = array[i++];
// 如果右边的子数组中剩余的数据写入数组中
while (j < high) res[k++] = array[j++];
// 把结果数组res中[low,high)中的部分复制到原数组中,从而改变原数组
for (i = low, k = low; i < high; i++, k++) {
array[k] = res[i];
}
}
public void mergeSort(int[] array, int low, int high) {
if (low >= high) return;
int mid = (low + high) / 2; // 将数组一分为二
mergeSort(array, 0, mid); // 对左边一半进行排序
mergeSort(array, mid, high); // 对右边一半进行排序
merge(array, low, mid, high); // 将排好序的两部分合并为一个排好序的数组
}
mergeSort()
是一个递归函数。递归的过程就像是一个下楼梯的过程。在递归时,调用一次自己,就下一个台阶(调用),直到下到最后一个(终止条件),然后想上返回到第一个台阶(回溯)。
归并排序,向下划分,向上合并的过程。在向下调用自身的过程,把数组一分为二,一次次的划分,直到数组中的一个元素单独成为一个子数组。mergeSort(array, low, mid)
和mergeSort(array, mid, high)
是调用自身划分的过程。合并是发生在划分之后,merge(array, low, mid, high)
是把两个升序的子数组合并为一个升序的数组,也是递归中回溯做的事情。