归并排序 Java

1 排序原理

归并排序是分治法的一个典型应用,简单来说,归并排序分为两部分,第一部分是拆,就是将数组的长度一层一层往下拆,第二部分就是将拆好的一个一个相邻的数据进行合并,在这一步里面我们进行排序。整体思想其实非常简单 

2 时间复杂度

O(logn*n)
正如其他的高级排序算法一样,归并排序其实也就是减少我们进行排序的重复操作。拆分的时候,采用二分的方式,所以有logn层,而每层比较的次数就是n喽,所以其时间复杂度为O(logn*n),
其实仔细想一想,所有的排序算法其本质差不多啦,就是较少重复操作,而如何减少重复操作呢,就是让我之前的操作对后序操作产生影响,这一点,归并排序比较明显,在其合并的时候,我合并的一个一个相邻的子数组,其实其内部是有序的,这也就是为什么归并排序效率高的原因了
    额外说一句,外排序的基础也是归并排序 

3 算法实现

/**
 * 归并排序--分为两部分 拆:递归拆 合:进行排序
 * 
 * @author xld
 *
 */
public class MergeSort {

    public static void sort(int[] arr) {
        int n = arr.length;
        sort(arr, 0, n - 1);
    }

    /**
     * 注意 这里的数组的取值范围为[l,r],也就是说,左右下标都可以取到
     * 
     * @param arr
     *            数组
     * @param l
     *            数组左下标
     * @param r
     *            数组右下标
     */
    private static void sort(int[] arr, int l, int r) {
        // 递归出口
        if (l >= r) {
            return;
        }
        int mid = (l + r) / 2;
        sort(arr, l, mid);
        sort(arr, mid + 1, r);
        merge(arr, l, mid, r);
    }

    /**
     * 合:真正将数据进行排序 将两个相邻的数组进行合并 是相邻的哦
     * 
     * @param arr
     * @param l
     * @param mid
     * @param r
     */
    private static void merge(int[] arr, int l, int mid, int r) {
        // 创建辅助数组
        int[] aux = new int[r - l + 1];
        // 辅助数组下标
        int auxIndex = 0;
        // 初始化辅助数组
        for (int i = l; i <= r; i++) {
            aux[auxIndex++] = arr[i];
        }
        // 左数组的下标索引
        int leftIndex = l;
        // 右数组的下标索引
        int rightIndex = mid + 1;
        for (int i = l; i <= r; i++) {
            // 如果左半部分元素已经全部处理完毕
            if (leftIndex > mid) {
                arr[i] = aux[rightIndex-l];
                rightIndex++;
            } else if (rightIndex > r) {
                arr[i] = aux[leftIndex-l];
                leftIndex++;
                // 小于等于 这个是归并排序稳定性的关键哦!
            } else if (aux[leftIndex-l] <= aux[rightIndex-l]) {
                // 当然也可以将两句直接写成arr[i]=aux[leftIndex++];我这里是为了好理解
                arr[i] = aux[leftIndex-l];
                leftIndex++;
            } else {
                arr[i] = aux[rightIndex-l];
                rightIndex++;
            }
        }
    }

    /**
     * 测试
     * @param args
     */
     public static void main(String[] args) {

            int[] arr = {10,9,8,7,6,5,4,3,2,1};
            MergeSort.sort(arr);
            for( int i = 0 ; i < arr.length ; i ++ ){
                System.out.print(arr[i]);
                System.out.print(' ');
            }
            System.out.println();
        }
}

4 优化

1 当数据量比较小的时候,可以采用插入排序进行优化,这是因为小的数据量,进行多次递归的消耗就犯不上了
2 当数据是基本有序的情况下,在合并的时候可以进行判断,如果前一个数组的最后一个元素比后一个数组的第一个元素还要小,那么就可以直接合并,无需比较了
### 归并排序Java中的实现 归并排序是一种经典的分治算法,其基本原理是将数组分成较小的子数组直到每个子数组仅有一个元素,之后再逐步合并这些有序的子数组来形成最终的大规模有序数组。下面是一个简单的归并排序实现: ```java public class MergeSort { public static void main(String[] args) { int[] array = {38, 27, 43, 3, 9, 82, 10}; mergeSort(array, 0, array.length - 1); for (int i : array) { System.out.print(i + " "); } } private static void mergeSort(int[] nums, int left, int right) { if (left < right) { int mid = (left + right) / 2; mergeSort(nums, left, mid); // 对左半边进行递归排序 mergeSort(nums, mid + 1, right);// 对右半边进行递归排序 merge(nums, left, mid, right); // 合并两个已排序的部分 } } private static void merge(int[] nums, int left, int mid, int right) { int n1 = mid - left + 1; int n2 = right - mid; /* 创建临时数组 */ int[] L = new int[n1]; int[] R = new int[n2]; /* 将数据复制到临时数组 */ for (int i = 0; i < n1; ++i) L[i] = nums[left + i]; for (int j = 0; j < n2; ++j) R[j] = nums[mid + 1 + j]; /* 合并临时数组 */ // 初始化指针 int i = 0, j = 0; // 初始索引k=left int k = left; while (i < n1 && j < n2) { if (L[i] <= R[j]) { nums[k] = L[i]; i++; } else { nums[k] = R[j]; j++; } k++; } /* 复制剩余的L[]元素,如果有的话 */ while (i < n1) { nums[k] = L[i]; i++; k++; } /* 复制剩余的R[]元素,如果有的话 */ while (j < n2) { nums[k] = R[j]; j++; k++; } } } ``` 此代码展示了如何通过创建辅助函数`merge()`来进行两部分之间的合并操作以及核心逻辑所在的`mergeSort()`方法[^1]。 尽管现代版本的Java标准库中对于`Arrays.sort()`和`Collections.sort()`采用了更高效的TimSort作为默认排序方式[^2],上述传统形式的归并排序依然具有重要的学习价值,并且适用于教学目的或者特定场景下的应用开发需求[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值