归并排序:原理、实现、优化与应用

归并排序:原理、实现、优化与应用

在 Java 编程的算法体系中,归并排序是一种经典且高效的排序算法。它基于分治思想,能将复杂的排序问题分解为简单的子问题逐一解决。接下来,让我们深入了解归并排序。

一、归并排序的原理

归并排序的核心是 “分而治之”。它将一个数组不断地分成两个子数组,直到子数组的长度为 1(因为长度为 1 的数组天然有序)。然后,再将这些有序的子数组合并成一个更大的有序数组,重复这个合并过程,最终将整个数组排序。

具体步骤如下:

  1. 分解:将待排序数组从中间分成两个子数组,不断递归地对这两个子数组进行分解,直到子数组只有一个元素。
  1. 合并:将两个有序的子数组合并成一个更大的有序数组。在合并过程中,通过比较两个子数组的元素,将较小的元素依次放入结果数组中,直到其中一个子数组的元素全部放入结果数组,再将另一个子数组剩余的元素直接追加到结果数组末尾。

二、Java 代码实现

public class MergeSort {

    public static void main(String[] args) {

        int[] array = {
            64,
            34,
            25,
            12,
            22,
            11,
            90
        };

        mergeSort(array);

        for (int num: array) {

            System.out.print(num + " ");

        }

    }

    public static void mergeSort(int[] arr) {

        if (arr == null || arr.length <= 1) {

            return;

        }

        int[] temp = new int[arr.length];

        mergeSortHelper(arr, temp, 0, arr.length - 1);

    }

    private static void mergeSortHelper(int[] arr, int[] temp, int left, int right) {

        if (left < right) {

            int mid = (left + right) / 2;

            // 递归地对左右子数组进行排序

            mergeSortHelper(arr, temp, left, mid);

            mergeSortHelper(arr, temp, mid + 1, right);

            // 合并两个有序子数组

            merge(arr, temp, left, mid, right);

        }

    }

    private static void merge(int[] arr, int[] temp, int left, int mid, int right) {

        int i = left;

        int j = mid + 1;

        int k = left;

        while (i <= mid && j <= right) {

            if (arr[i] <= arr[j]) {

                temp[k] = arr[i];

                i++;

            } else {

                temp[k] = arr[j];

                j++;

            }

            k++;

        }

        // 将剩余元素复制到临时数组

        while (i <= mid) {

            temp[k] = arr[i];

            i++;

            k++;

        }

        while (j <= right) {

            temp[k] = arr[j];

            j++;

            k++;

        }

        // 将临时数组中的内容复制回原数组

        for (i = left; i <= right; i++) {

            arr[i] = temp[i];

        }

    }

}

三、性能分析

  1. 时间复杂度:归并排序在分解和合并过程中,每层的时间复杂度为 O (n),而递归深度为 log n,所以总的时间复杂度为 O (n log n),无论数组初始状态如何,这个时间复杂度都保持不变。
  1. 空间复杂度:在合并过程中需要一个临时数组来辅助合并,其大小与原数组相同,所以空间复杂度为 O (n)。

四、优化策略

  1. 减少临时数组的创建次数:在上述代码中,每次递归调用mergeSortHelper时都创建了一个临时数组。可以在最外层调用时创建一次临时数组,然后传递给递归函数,这样可以减少内存分配和回收的开销。
public class OptimizedMergeSort {

    public static void main(String[] args) {

        int[] array = {
            64,
            34,
            25,
            12,
            22,
            11,
            90
        };

        int[] temp = new int[array.length];

        mergeSort(array, temp);

        for (int num: array) {

            System.out.print(num + " ");

        }

    }

    public static void mergeSort(int[] arr, int[] temp) {

        if (arr == null || arr.length <= 1) {

            return;

        }

        mergeSortHelper(arr, temp, 0, arr.length - 1);

    }

    private static void mergeSortHelper(int[] arr, int[] temp, int left, int right) {

        if (left < right) {

            int mid = (left + right) / 2;

            mergeSortHelper(arr, temp, left, mid);

            mergeSortHelper(arr, temp, mid + 1, right);

            merge(arr, temp, left, mid, right);

        }

    }

    private static void merge(int[] arr, int[] temp, int left, int mid, int right) {

        // 合并逻辑与之前相同

    }

}
  1. 对小规模数组使用插入排序:当子数组的规模较小时,插入排序在局部数据上的性能优于归并排序。可以在递归分解时,设置一个阈值,当子数组长度小于阈值时,直接使用插入排序。
public class HybridMergeSort {

    private static final int INSERTION_SORT_THRESHOLD = 16;

    public static void main(String[] args) {

        int[] array = {
            64,
            34,
            25,
            12,
            22,
            11,
            90
        };

        int[] temp = new int[array.length];

        mergeSort(array, temp);

        for (int num: array) {

            System.out.print(num + " ");

        }

    }

    public static void mergeSort(int[] arr, int[] temp) {

        if (arr == null || arr.length <= 1) {

            return;

        }

        mergeSortHelper(arr, temp, 0, arr.length - 1);

    }

    private static void mergeSortHelper(int[] arr, int[] temp, int left, int right) {

        if (left < right) {

            if (right - left + 1 <= INSERTION_SORT_THRESHOLD) {

                insertionSort(arr, left, right);

                return;

            }

            int mid = (left + right) / 2;

            mergeSortHelper(arr, temp, left, mid);

            mergeSortHelper(arr, temp, mid + 1, right);

            merge(arr, temp, left, mid, right);

        }

    }

    private static void merge(int[] arr, int[] temp, int left, int mid, int right) {

        // 合并逻辑与之前相同

    }

    private static void insertionSort(int[] arr, int left, int right) {

        for (int i = left + 1; i <= right; i++) {

            int key = arr[i];

            int j = i - 1;

            while (j >= left && arr[j] > key) {

                arr[j + 1] = arr[j];

                j = j - 1;

            }

            arr[j + 1] = key;

        }

    }

}

五、与其他排序算法的对比

  1. 与快速排序相比:快速排序的平均时间复杂度也是 O (n log n),但在最好情况下,快速排序的时间复杂度可以达到 O (n),且快速排序是原地排序,空间复杂度为 O (log n)。而归并排序的时间复杂度更稳定,在处理大规模数据时,快速排序的性能通常优于归并排序,但在数据规模较小或对稳定性有要求时,归并排序可能更合适。
  1. 与希尔排序相比:希尔排序的时间复杂度依赖于步长序列,在某些情况下性能不如归并排序稳定。归并排序的时间复杂度始终为 O (n log n),且是稳定排序,而希尔排序在一些步长序列下不是稳定排序。

归并排序以其稳定的性能和清晰的分治思想,在 Java 编程中有着广泛的应用。通过对其原理、实现和优化的学习,我们能更好地在实际项目中运用它来解决排序问题。如果你对归并排序还有其他疑问,比如在多线程环境下的应用,欢迎随时交流。

​ 如果需要详细的动态排序过程,可以参考下面这个网站,这个网站能看到排序的动态过程

排序(冒泡排序,选择排序,插入排序,归并排序,快速排序,计数排序,基数排序) - VisuAlgohttps://visualgo.net/zh/sorting

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值