(二分)归并排序,其实就是两两合并,保证每一组有序(开始时每组只有一个元素肯定有序),两组合并后保证有序。因为合并时有对比过程,分别将两组中较小的先放入合并组,两组中有一组没有元素后,另一组元素直接拿到合并组。最终合成的一个大组,即整个数组,也是有序的。
递归实现:
/**
* 归并排序(递归)
* @param array 待排序数组
*/
private static void mergeSort(int[] array) {
if (array==null || array.length<2) {
return;
}
mergeSort(array, 0 , array.length-1);
}
/**
* 根据边界归并排序边界内部分
* @param array 待排序数组
* @param l 左边界(含)
* @param r 右边界(含)
*/
private static void mergeSort(int[] array, int l, int r) {
if (l >= r) return;
// 中间位置不用 l+r/2 的原因是数组很大时,l+r会溢出
int mid = l + ((r-l) >> 1);
// 左部分排序
mergeSort(array, l, mid);
// 右部分排序
mergeSort(array, mid+1, r);
// 两个部分排序后的结果进行合并
merge(array, l, mid, r);
}
/**
* 两个排序好的部分进行合并成一个有序部分
* @param array 数组
* @param l 左边界(含)
* @param mid 中间位置角标
* @param r 右边界(含)
*/
private static void merge(int[] array, int l, int mid, int r) {
int[] help = new int[r-l+1];
int index = 0;
int leftCursor = l;
int rightCursor = mid+1;
/*游标在两部分都未越界*/
while (leftCursor <= mid && rightCursor<=r) {
if (array[leftCursor] < array[rightCursor]) {
help[index++] = array[leftCursor++];
} else if (array[leftCursor] > array[rightCursor]) {
help[index++] = array[rightCursor++];
} else {
help[index++] = array[leftCursor++];
help[index++] = array[rightCursor++];
}
}
/*右部分越界,左部分剩余的值直接拿过来*/
while (leftCursor <= mid) {
help[index++] = array[leftCursor++];
}
/*左部分越界,右部分剩余的直接拿过来*/
while (rightCursor <= r) {
help[index++] = array[rightCursor++];
}
/*辅助数组替换原有数组*/
index=0;
while (l <= r) {
array[l++] = help[index++];
}
}
非递归实现:
/**
* 归并排序(非递归)
* @param array 待排序数组
*/
private static void mergeSortV2(int[] array){
if (array==null || array.length<2) {
return;
}
int length = array.length;
int groupSize = 1;// 分组大小
/*分组大小大于或等于就不用排序合并了*/
while (groupSize < length) {
int groupNum = length/groupSize;// 判断可以分多少组。需要注意可能后面还有不够一组的部分
boolean moreGroup = length % groupSize > 0;// 是否还有不够一组的
int l = 0;
for (int i = 0; i < groupNum; i+=2) {
int mid = l + groupSize - 1, r = mid + groupSize;// 左右边界
if (r > length-1) {
if (! moreGroup) {
break;
} else {
r = length-1;
}
}
merge(array, l, mid, r);
l = r + 1;
}
/*为防止groupSize溢出,需要做个判断*/
if (groupSize > (length >> 1)) {
break;
} else {
groupSize <<= 1;
}
}
}
非递归排序的难点:需要控制数组的边界,分组大小的边界。
两种递归和非递归的实现对比,从中可以发现,这两种代码其实做的是一件事,归并排序执行的顺序是一样的,但是思考的过程是相反的。