0
点赞
收藏
分享

微信扫一扫

数据结构/排序/归并排序/二路归并排序

两岁时就很帅 2022-03-27 阅读 44

原理

图解:一颗二叉树,叶子结点为单个记录,到最后1层后开始两两合并
在这里插入图片描述

介绍
二路归并排序

  1. 子序列长度从1到n/2(把数组划分为2个子序列)
  2. 从左往右一次比较2个子序列

非递归实现

  1. 子序列长度,h从1开始到?,作2倍变化2h
  2. 子序列个数,根据剩余子序列的个数执行相应的操作(剩余子序列个数大于2;剩余子序列个数大于1小于2;剩余子序列个数小于1)
  3. 记录个数,根据子序列中记录个数执行相应的操作(两个子序列中都有记录;子序列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]);
        }
    }
}

举报

相关推荐

0 条评论