在诸多排序算法中,归并排序号称是仅次于快速排序的一种算法。
其算法原理是,不断地将待排序数组划分为两部分,直到子数组不能再分时,再对子数组两两之间进行有序合并Merge(实现一步小范围排序),合并后的多个数组继续进行两两的有序合并Merge。不断将子数组合并为原始长度的大数组,此时的大数组即是归并排序得到的数组。
归并排序的过程就是一个通过递归来求解的过程,先不断进行问题分解(stage1 递推),即两个子数组的排序(及子数组的子数组的排序),最后再将排序的子数组合并为原数组的排序数组(stage2 回归)。
但递归往往效率较低,那么怎么用非递归来实现归并排序呢?
这里的归并排序的子问题分解,其实是一个广度搜索的问题,我们可以借助两个队列Queue的数据结构来实现。
以下是Java编写的递归版归并排序和非递归版归并排序。
import java.util.*;
public class Sort {
public static void main(String[] args) {
Sort main = new Sort();
//测试
int[] array = {12, 1, 5, 9, 8, 6, 2, 5, 5, 6, 9, 10, 3};
System.out.println("Before Sort: " + Arrays.toString(array));
main.mergeSort(array);
System.out.println("After Sort: " + Arrays.toString(array));
}
private void mergeSort(int[] arr) {
mergeSortLoop(arr, 0, arr.length - 1);
}
/* 递归版本 */
private void mergeSortRecur(int[] arr, int left, int right) {
if (left >= right) return;
int mid = (left + right) >> 1;//中点
mergeSortRecur(arr, left, mid);
mergeSortRecur(arr, mid + 1, right);
merge(arr, left, mid, right);
}
/* 非递归版本 */
private void mergeSortLoop(int[] arr, int left, int right) {
if (left >= right) return;
LinkedList<int[]> queue4M = new LinkedList<>();//用于记录数组合并的节点位置
Queue<int[]> queue = new LinkedList<>();//用于子问题分解
queue.offer(new int[]{left, right});
while (!queue.isEmpty()) {
int[] pair = queue.poll();
left = pair[0];
right = pair[1];
int mid = (left + right) >> 1;
if (left < right) {//继续入栈 [1 2]分割 [1]不分割
queue.offer(new int[]{left, mid});
queue.offer(new int[]{mid + 1, right});
queue4M.add(new int[]{left, mid, right});//
}
}
while (!queue4M.isEmpty()) {
int[] pair = queue4M.removeLast();//重要,必须从队列尾部开始归并
merge(arr, pair[0], pair[1], pair[2]);
}
}
/*通用有序数组合并, 递归、非递归都要用到*/
private void merge(int[] arr, int left, int mid, int right) {
//(left, mid) (mid+1, right)
if (right == left) return;//一个数
int n = right - left + 1;//合并段的段程
int[] temp = new int[n];
int idl = left, idr = mid + 1, idt = 0;
while (idl <= mid && idr <= right) {
if (arr[idl] <= arr[idr]) {
temp[idt++] = arr[idl++];
} else {
temp[idt++] = arr[idr++];
}
}
while (idl <= mid) {
temp[idt++] = arr[idl++];
}
while (idr <= right) {
temp[idt++] = arr[idr++];
}
for (int i = 0; i < n; i++) {
arr[left + i] = temp[i];
}
System.out.println(Arrays.toString(temp));//排序和归并过程!
}
}
END 2020-9-4