九、归并排序(Merge Sort)
1、概念
使用归并
思想,将排序序列进行分-合
的操作,实现最终的排序。
分-合(devide-conquer):将问题分
为一些小问题,递归
(也可以采用迭代的方式)拆分子序列(完全二叉树
),各个求解,再将得到的各个答案,合
并在一起。
34158276
3415 8276
34 15 82 76
3 4 1 5 8 2 7 6
34 15 28 67
1534 2867
12345678
递归方式
:准备一个和原排序序列长度一样的,临时辅助排序序列,找到中间索引,比较两端元素值大小,小的放到临时排序序列前面,大的放到临时排序序列后面(正序),最后再将临时排序中的元素值,依次覆盖掉本次排序比较序列的元素值。
2、过程
- 创建一个临时排序序列,长度等于待排序序列的长度。
- 分的方法:每次都找到中间索引值,并递归调用分的方法,起始索引到中间索引,中间索引+1到终止索引,最后调用合的方法。
- 合的方法:左端比较索引从起始索引开始,右端比较索引从中间索引的下一位开始,当左端比较索引和右端比较索引,均未到达中间索引和终止索引时,比较中间索引值两端的元素值大小,将较小的元素值赋值给临时排序数组,并右移左端比较索引和右端比较索引,再将未比较完的左端元素值或右端赋值给临时排序数组。最后再将临时排序数组的元素,依次覆盖掉本次比较排序序列的部分元素值(从起始索引到终止索引)。
3、示例
正序
public void mergeSort(int[] array) {
int len = array.length;
int[] temp = new int[len];
mergeSort(array, 0, len - 1, temp);
}
private void mergeSort(int[] array, final int begin, final int end, int[] temp) {
if (begin < end) {
int middle = (begin + end) >> 1;
mergeSort(array, begin, middle, temp);
mergeSort(array, middle + 1, end, temp);
merge(array, begin, middle, end, temp);
}
}
private void merge(int[] array, final int begin, final int middle, final int end, int[] temp) {
int b = begin;
int m = middle + 1;
int index = 0;
while (b <= middle && m <= end) { // 起始-中间,中间+1-终止
if (array[b] <= array[m]) { // 比较两端大小
temp[index] = array[b];
index++;
b++;
} else {
temp[index] = array[m];
index++;
m++;
}
}
while (b <= middle) { // 将左端剩余部分添加到临时数组中
temp[index] = array[b];
index++;
b++;
}
while (m <= end) { // 将右端剩余部分添加到临时数组中
temp[index] = array[m];
index++;
m++;
}
b = begin;
index = 0;
while (b <= end) { // 重新赋值:将临时数组中的元素赋值给原始数组
array[b] = temp[index];
b++;
index++;
}
}
倒序
public void mergeSort(int[] array) {
int len = array.length;
int[] temp = new int[len];
mergeSort(array, 0, len - 1, temp);
}
private void mergeSort(int[] array, final int begin, final int end, int[] temp) {
if (begin < end) {
int middle = (begin + end) >> 1;
mergeSort(array, begin, middle, temp);
mergeSort(array, middle + 1, end, temp);
merge(array, begin, middle, end, temp);
}
}
private void merge(int[] array, final int begin, final int middle, final int end, int[] temp) {
int b = begin;
int m = middle + 1;
int index = 0;
while (b <= middle && m <= end) {
if (array[b] >= array[m]) { // 倒序
temp[index++] = array[b++];
} else {
temp[index++] = array[m++];
}
}
while (b <= middle || m <= end) {
if (b <= middle) {
temp[index++] = array[b++];
} else {
temp[index++] = array[m++];
}
}
b = begin;
index = 0;
while (b <= end) {
array[b++] = temp[index++];
}
}
4、性能
- 时间复杂度:平均时间复杂度
O(nlog₂n)
,最坏时间复杂度O(nlog₂n)
- 空间复杂度:
O(nlog₂n) 因为尾递归
- 稳定性:不稳定(a=b,排序前a在b的前面,排序后a不一定还在b的前面)