分析归并排序之前,我们先来了解一下分治算法。
分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。
分治算法的一般步骤:
- 分解,将要解决的问题划分成若干规模较小的同类问题;
- 求解,当子问题划分得足够小时,用较简单的方法解决;
- 合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。
归并排序先将一个无序的N长数组切成N个有序子序列(只有一个数据的序列认为是有序序列),
然后两两合并,再将合并后的N/2(或者N/2 + 1)个子序列继续进行两两合并,以此类推得到一个完整的有序数组。过程如下图所示:
归并排序是分治算法的典型应用。
归并排序算法稳定,数组需要O(n)的额外空间,链表需要O(log(n))的额外空间,时间复杂度为O(nlog(n)),算法不是自适应的,不需要对数据的随机读取。
/**
* 归并排序,主要是做了拆分之后再排序的操作。
*
* @param arrays 待排序的数组
* @param start 指向数组第一个元素
* @param end 指向数组最后一个元素
*/
public static void mergeSort(int[] arrays, int start, int end) {
// 如果只有一个元素,那就不用排序了
if (start == end) {
System.out.println("数组拆分已经结束。");
} else {
// 取中间的数,进行拆分
int middle;
// 左少右多的方式
if ((start + end) % 2 == 0) {
middle = (start + end) / 2 - 1;
} else {
middle = (start + end) / 2;
}
// 左多右少的方式
// middle = (start + right) / 2;
// 左边的数不断进行拆分
mergeSort(arrays, start, middle);
// 右边的数不断进行拆分
mergeSort(arrays, middle + 1, end);
// 合并
merge(arrays, start, middle + 1, end);
}
}
/**
* 合并数组
*
* @param arrays 排好序的素组
* @param start 指向数组第一个元素
* @param middle 指向数组分隔的元素
* @param end 指向数组最后的元素
*/
public static void merge(int[] arrays, int start, int middle, int end) {
// 左边的数组的大小
int[] leftArray = new int[middle - start];
// 右边的数组大小
int[] rightArray = new int[end - middle + 1];
// 往这两个数组填充数据
for (int i = start; i < middle; i++) {
leftArray[i - start] = arrays[i];
}
for (int i = middle; i <= end; i++) {
rightArray[i - middle] = arrays[i];
}
System.out.println("leftArray + rightArray两个数组的值:" + leftArray + rightArray);
int i = 0, j = 0;
// arrays数组的第一个元素
int k = start;
//比较这两个数组的值,哪个小,就往数组上放
while (i < leftArray.length && j < rightArray.length) {
// 谁比较小,谁将元素放入大数组中,移动指针,继续比较下一个
// 等于的情况是保证“稳定”
if (leftArray[i] <= rightArray[j]) {
arrays[k] = leftArray[i];
i++;
k++;
} else {
arrays[k] = rightArray[j];
j++;
k++;
}
}
// 如果左边的数组还没比较完,右边的数都已经完了,那么将左边的数抄到大数组中(剩下的都是大数字)
while (i < leftArray.length) {
arrays[k] = leftArray[i];
i++;
k++;
}
// 如果右边的数组还没比较完,左边的数都已经完了,那么将右边的数抄到大数组中(剩下的都是大数字)
while (j < rightArray.length) {
arrays[k] = rightArray[j];
k++;
j++;
}
}