算法思想
把待排序数组进行二分,左边归并排序,右边归并排序;然后再两者归并
如果左(或右)边还是无序的,那么对左(或右)再进行二分,分成更小的左和右;左边归并,右边归并,然后再两者归并
...
依次类推
那程序是怎么知道分到什么时候“正好”左边有序且右边也有序呢?
答案是不知道(人可以看一眼待排数组并作出像“前三个有序,后三个有序”的判断),所以是分到最小,即紧邻的“两个”。所以站在程序的角度就是
对待排序数组进行二分,再二分......直到分到最小;
然后再从最小的一对左和右进行归并,成为一个较大的左(或右);较大的一对左和右再进行归并,成为更大的左(或右)
...
直到最后两个最大的左和最大的右进行归并,生成排好序的整个数组
代码实现
public static void main(String[] args) {
int[] arr = {2, 5, 6, 8, 9, 7, 4, 1, 3, 0, 11, 15, 13, 18, 14, 16, 19, 17, 12, 10, 20};
// 要归并排序的数组是arr,要排序整个数组
sort(arr, 0, arr.length - 1);
System.out.println("最终结果:" + Arrays.toString(arr));
}
/**
* 递归排序
* @param arr 要排序的数组
* @param left 左半部分的起始索引位置
* @param right 最右(右边界)的索引位置
*/
private static void sort(int[] arr, int left, int right) {
// 递归的终止条件
if (left == right) {
return;
}
// 中间索引位置(有人说,(left + right) / 2 这种算法不好)
int mid = left + (right -left) /2;
// 左半部分排序(包含中间索引对应的值)
sort(arr, left, mid);
// 右半部分排序
sort(arr, mid + 1, right);
// 归并
merge(arr, left, right, mid);
}
/**
* 归并操作
* @param arr 要排序的数组
* @param left 左半部分的起始索引位置
* @param right 最右(右边界)的索引位置
* @param mid 二分的中间索引位置
*/
private static void merge(int[] arr, int left, int right, int mid) {
// 临时数组
int[] tmp = new int[right - left + 1];
// 左半部分的开始位置,该指针指向这个位置
int i = left;
// 右半部分的开始位置,该指针指向这个位置
int j = mid + 1;
// 临时数组的起始位置,该指针指向这个位置
int k = 0;
if (left > j) {
throw new RuntimeException("左指针索引大于有指针索引");
}
if (left == j) {
return;
}
// 左边且右边指针未到头,则把数据放到临时数组
while (i <= mid && j <= right) {
// 归并,依次把左右两半中较小的放入临时数组
tmp[k++] = (arr[i] <= arr[j]) ? arr[i++] : arr[j++];
}
// 右边指针到头,如果左边还有,则左半部分剩余放到临时数组
while (i <= mid) {
tmp[k++] = arr[i++];
}
// 左边指针到头,如果右半部分还有,则右半部分剩余放到临时数组
while (j <= right) {
tmp[k++] = arr[j++];
}
/* 把临时数组的数据(已排好序)按索引对应的位置“重置”原数组;
注意,对应的位置不一定是0对应0,1对应1
*/
for (int index = 0; index < tmp.length; index ++) {
arr[left++] = tmp[index];
}
// 打印每次归并临时数组的数据
System.out.println(Arrays.toString(tmp));
// 打印每次归并重置后的原数组
System.out.println(Arrays.toString(arr));
}
程序运行结果
tmp:[2, 5]
arr:[2, 5, 6, 8, 9, 7, 4, 1, 3, 0, 11, 15, 13, 18, 14, 16, 19, 17, 12, 10, 20]
tmp:[2, 5, 6]
arr:[2, 5, 6, 8, 9, 7, 4, 1, 3, 0, 11, 15, 13, 18, 14, 16, 19, 17, 12, 10, 20]
tmp:[8, 9]
arr:[2, 5, 6, 8, 9, 7, 4, 1, 3, 0, 11, 15, 13, 18, 14, 16, 19, 17, 12, 10, 20]
tmp:[7, 8, 9]
arr:[2, 5, 6, 7, 8, 9, 4, 1, 3, 0, 11, 15, 13, 18, 14, 16, 19, 17, 12, 10, 20]
tmp:[2, 5, 6, 7, 8, 9]
arr:[2, 5, 6, 7, 8, 9, 4, 1, 3, 0, 11, 15, 13, 18, 14, 16, 19, 17, 12, 10, 20]
tmp:[1, 4]
arr:[2, 5, 6, 7, 8, 9, 1, 4, 3, 0, 11, 15, 13, 18, 14, 16, 19, 17, 12, 10, 20]
tmp:[1, 3, 4]
arr:[2, 5, 6, 7, 8, 9, 1, 3, 4, 0, 11, 15, 13, 18, 14, 16, 19, 17, 12, 10, 20]
tmp:[0, 11]
arr:[2, 5, 6, 7, 8, 9, 1, 3, 4, 0, 11, 15, 13, 18, 14, 16, 19, 17, 12, 10, 20]
tmp:[0, 1, 3, 4, 11]
arr:[2, 5, 6, 7, 8, 9, 0, 1, 3, 4, 11, 15, 13, 18, 14, 16, 19, 17, 12, 10, 20]
tmp:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11]
arr:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 15, 13, 18, 14, 16, 19, 17, 12, 10, 20]
tmp:[13, 15]
arr:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 18, 14, 16, 19, 17, 12, 10, 20]
tmp:[13, 15, 18]
arr:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 18, 14, 16, 19, 17, 12, 10, 20]
tmp:[14, 16]
arr:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 18, 14, 16, 19, 17, 12, 10, 20]
tmp:[13, 14, 15, 16, 18]
arr:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 14, 15, 16, 18, 19, 17, 12, 10, 20]
tmp:[17, 19]
arr:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 14, 15, 16, 18, 17, 19, 12, 10, 20]
tmp:[12, 17, 19]
arr:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 14, 15, 16, 18, 12, 17, 19, 10, 20]
tmp:[10, 20]
arr:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 14, 15, 16, 18, 12, 17, 19, 10, 20]
tmp:[10, 12, 17, 19, 20]
arr:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 14, 15, 16, 18, 10, 12, 17, 19, 20]
tmp:[10, 12, 13, 14, 15, 16, 17, 18, 19, 20]
arr:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20]
tmp:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
arr:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
最后结果:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
复杂度和稳定性
时间复杂度
- 最坏时间复杂度:O()
- 最好时间复杂度:O()
- 平均时间复杂度:O()
空间复杂度:O(n)
稳定性:稳定