归并排序的步骤就是分组->合并,这两步均是递归进行,直到所操作的子组仅有一个元素为止。
例如一个数组:
6, 3, 1, 9, 2, 7, 4, 5
首先分成两组:
[6, 3, 1, 9] [2, 7, 4, 5]
对于左边,再次分组:
[6, 3] [1, 9]
再对左边分组:
[6] [3]
因为各自仅包含一个元素了,这一个元素组成的字数组就是有序的了。接下来向上递归合并:
[6] [3] -> [3, 6]
[1] [9] -> [1, 9]
[3, 6] [1, 9] -> [1, 3, 6, 9]
对于右半边子数组同样:
[2, 4, 5, 7]
最后再合并:
[1, 3, 6, 9] [2, 4, 5, 7] -> [1, 2, 3, 4, 5, 7, 9]
分组的过程比较简单,直接将数组拆成两部分即可。
合并时,需要交替比较两个数组间的元素,由于合并时,两个子数组均已经排序好了,因此两个数组从头至尾遍历一次即可。
从一个数组A开始,当其中元素开始大于另一数组B的元素时,就停止遍历A,开始遍历B中的元素,当B元素开始比A中的大时,反之继续。
[(1), 3, 6, 9] [<2>, 4, 5, 7] -> [1, _, _, _, _, _, _, _]
[1, <3>, 6, 9] [(2), 4, 5, 7] -> [1, 2, _, _, _, _, _, _]
[1, (3), 6, 9] [2, <4>, 5, 7] -> [1, 2, 3, _, _, _, _, _]
[1, 3, <6>, 9] [2, (4), 5, 7] -> [1, 2, 3, 4, _, _, _, _]
[1, 3, <6>, 9] [2, 4, (5), 7] -> [1, 2, 3, 4, 5, _, _, _]
[1, 3, (6), 9] [2, 4, 5, <7>] -> [1, 2, 3, 4, 5, 6, _, _]
[1, 3, 6, <9>] [2, 4, 5, (7)] -> [1, 2, 3, 4, 5, 6, 7, _]
[1, 3, 6, <9>] [2, 4, 5, 7]() -> [1, 2, 3, 4, 5, 6, 7, 9]
以下是Java版本代码:
package com.example.mergesort;
public class MergeSort {
/*
* 顶层的接口,用于外部调用
* */
public static void sortA(int[] arrayToSort) {
int leftIndexStart;
int leftIndexEnd;
int rightIndexStart;
int rightIndexEnd;
// 在本例中,顶层接口不做数组大小判断了。在此直接获取各下标参数
int midIndex = arrayToSort.length / 2;
leftIndexStart = 0;
leftIndexEnd = midIndex;
rightIndexStart = midIndex + 1;
rightIndexEnd = arrayToSort.length - 1;
// 声明一个与原数组一样大小的空组,用于合并时存储有序数据
int[] tmp = new int[arrayToSort.length];
// 递归调用,对左侧子数组继续分组
sortPart(arrayToSort, tmp, leftIndexStart, leftIndexEnd);
// 递归调用,对右侧子数组继续分组
sortPart(arrayToSort, tmp, rightIndexStart, rightIndexEnd);
// 合并左右两个子组
merge(arrayToSort, tmp, leftIndexStart, leftIndexEnd, rightIndexStart, rightIndexEnd);
}
/*
* 接收临时存储数组及上下界,用于内部递归调用
* */
public static void sortPart(int[] array, int[] tmp, int left, int right) {
int leftIndexStart;
int leftIndexEnd;
int rightIndexStart;
int rightIndexEnd;
// 当子数组长度小于1时,直接返回
if ((right - left) < 1) {
return;
}
// 获取各下标参数
int midIndex = (right + left) / 2;
leftIndexStart = left;
leftIndexEnd = midIndex;
rightIndexStart = midIndex + 1;
rightIndexEnd = right;
// 继续对左侧分组
sortPart(array, tmp, leftIndexStart, leftIndexEnd);
// 继续对右侧分组
sortPart(array, tmp, rightIndexStart, rightIndexEnd);
// 合并两个子组
merge(array, tmp, leftIndexStart, leftIndexEnd, rightIndexStart, rightIndexEnd);
}
/*
* 合并函数,使用了唯一的与原数组相同大小的空组。
* 由于所有合并步骤均在相对应的下标范围操作,因此元素并不会产生重叠。
* 最后的tmp也即为排序好的数组的拷贝。
* */
public static void merge(int[] array, int[] tmp, int leftStart, int leftEnd, int rightStart, int rightEnd) {
int leftIndex = leftStart;
int rightIndex = rightStart;
int tmpIndex = leftStart;
// 交替遍历左右两组,并改变相应下标。
while(leftIndex <= leftEnd && rightIndex <= rightEnd) {
if (array[leftIndex] < array[rightIndex]) {
tmp[tmpIndex] = array[leftIndex];
tmpIndex++;
leftIndex++;
} else {
tmp[tmpIndex] = array[rightIndex];
tmpIndex++;
rightIndex++;
}
}
// 交替遍历后,有可能一侧剩余下不定数量元素,继续将剩余元素填充进tmp
while(leftIndex <= leftEnd) {
tmp[tmpIndex] = array[leftIndex];
tmpIndex++;
leftIndex++;
}
// 交替遍历后,有可能一侧剩余下不定数量元素,继续将剩余元素填充进tmp
while(rightIndex <= rightEnd) {
tmp[tmpIndex] = array[rightIndex];
tmpIndex++;
rightIndex++;
}
// 将tmp中按顺序填充好的元素覆盖到原数组相应元素上
for(int i = leftStart; i <= rightEnd; i ++) {
array[i] = tmp[i];
}
}
public static void main(String[] args) {
int[] a = { 13, 12, 11, 23, 9, 8, 7, 6, 5, 4, 5, 2, 1 };
MergeSort.sortA(a);
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + ", ");
}
System.out.println();
}
}