原理
图解:一颗二叉树,叶子结点为单个记录,到最后1层后开始两两合并
介绍
二路归并排序
- 子序列长度从1到n/2(把数组划分为2个子序列)
- 从左往右一次比较2个子序列
非递归实现
- 子序列长度,h从1开始到?,作2倍变化2h
- 子序列个数,根据剩余子序列的个数执行相应的操作(剩余子序列个数大于2;剩余子序列个数大于1小于2;剩余子序列个数小于1)
- 记录个数,根据子序列中记录个数执行相应的操作(两个子序列中都有记录;子序列1中还有记录;子序列2中还有记录)
递归实现:使用递归控制子序列的长度和个数
- 记录个数,根据子序列中记录个数执行相应的操作(两个子序列中都有记录;子序列1中还有记录;子序列2中还有记录)
时间复杂度(非递归): n l o g 2 n nlog_2n nlog2n
- 一趟归并排序需要将待排序序列扫描一遍
- 2路归并需要进行 l o g 2 n log_2n log2n趟(一颗二叉树)
空间复杂度(非递归): O ( n ) O(n) O(n)
- 需要1个和待排序数组同样长的数字作为临时存储空间
稳定性(非递归):稳定,两个子序列相互比较不改变相对次序
时间复杂度(递归):
- 一趟归并排序需要将待排序序列扫描一遍
- 2路归并需要进行 l o g 2 n log_2n log2n趟(递归的深度)
空间复杂度(递归): O ( n ) O(n) O(n)
- 需要1个和待排序数组同样长的数字作为临时存储空间
- 压栈 l o g 2 n log_2n log2n
稳定性(递归):稳定,两个子序列相互比较不改变相对次序
代码
package cn.xcrj.data_structure;
import java.util.Scanner;
/**
* 二路归并排序:
* 1. 子序列长度从1到n/2(把数组划分为2个子序列)
* 2. 从左往右一次比较2个子序列
* <p>
* <p>
* 非递归实现
* 1. 子序列长度,h从1开始到?,作2倍变化2h
* 2. 子序列个数,根据剩余子序列的个数执行相应的操作
* 3. 记录个数,根据子序列中记录个数执行相应的操作
* <p>
* 递归实现:使用递归控制子序列的长度和个数
* 1. 记录个数,根据子序列中记录个数执行相应的操作
*/
public class Way2MergeSort {
/**
* 二路归并非递归实现
*
* @param r 输入数组
*/
public static void way2MergeSortG(int[] r) {
// 实际记录个数
int n = r.length;
// 临时数组-目标数组
int[] r1 = new int[n];
// 1. 子序列长度
// 初始子序列长度
int h = 1;
// r和r1交替作为归并结果存储空间,最终的结果在r中
while (h < n) {
mergePass(r, r1, h, n);
// 子序列长度变为原来的2倍
h *= 2;
}
}
/**
* 二路归并递归实现
*
* @param rs source 数组
* @param rd dest 数组
* @param s 子序列开始位置,包括
* @param e 子序列结束位置,不包括
*/
public static void way2MergeSortR(int[] rs, int[] rd, int s, int e) {
// e-1因为前包后不包
if (s < e - 1) {
// 继续往下两两划分
int m = (s + e) / 2;
way2MergeSortR(rs, rd, s, m);
way2MergeSortR(rs, rd, m, e);
merge(rs, rd, s, m, m, e);
}
}
/**
* 2. 子序列个数
*/
public static void mergePass(int[] rs, int[] rd, int h, int n) {
int i = 0;
// 剩余子序列个数大于2
while (i < n - 2 * h) {
// 开始2路归并
merge(rs, rd, i, i + h, i + h, i + 2 * h);
i += 2 * h;
}
// 剩余子序列个数大于1小于2
if (i < n - h) {
merge(rs, rd, i, i + h, i + h, n);
i += 2 * h;
}
// 剩余子序列个数小于1
for (; i < n; i++) rd[i] = rs[i];
}
/**
* 2. 元素个数,开始2路归并
*
* @param rs source 数组
* @param rd dest 数组
* @param s1 第1个子序列开始位置,包括
* @param s2 第2个子序列开始位置,包括
* @param e1 第1个子序列结束位置,不包括
* @param e2 第2个子序列结束位置,不包括
*/
public static void merge(int[] rs, int[] rd, int s1, int s2, int e1, int e2) {
int i1 = s1;
int i2 = s2;
int k = s1;
// 两个子序列中都有记录
while (i1 < e1 && i2 < e2) {
// 把小的记录放到rd中
if (rs[i1] <= rs[i2]) rd[k++] = rs[i1++];
else rd[k++] = rs[i2++];
}
// 子序列1中还有记录
while (i1 < e1) rd[k++] = rs[i1++];
// 子序列2中还有记录
while (i2 < e2) rd[k++] = rs[i2++];
// 将rd中排序好的结果放回rs,s1 < k因为k从s1开始即rd中已经有序的记录从s1开始到k(不包括)
for (; s1 < k; s1++) rs[s1] = rd[s1];
}
public static void main(String[] args) {
// 数组录入
System.out.println("请以空格隔开输入1个数组序列:");
System.out.println("示例:2 1 5 7 2 6");
Scanner s = new Scanner(System.in);
String inputStr = s.nextLine();
String[] strArray = inputStr.split(" ");
int[] r = new int[strArray.length];
for (int i = 0; i < r.length; i++) {
r[i] = Integer.parseInt(strArray[i]);
}
System.out.print("输入序列为:");
for (int i = 0; i < r.length; i++) {
System.out.print(" " + r[i]);
}
System.out.println();
// 调用非递归排序
way2MergeSortG(r);
// 调用递归排序
// int[] rd = new int[r.length];
// way2MergeSortR(r, rd, 0, r.length);
// 输出排序好的序列
System.out.print("排序后序列为:");
for (int i = 0; i < r.length; i++) {
System.out.print(" " + r[i]);
}
}
}