归并排序

14 篇文章 0 订阅
4 篇文章 0 订阅

归并排序的步骤就是分组->合并,这两步均是递归进行,直到所操作的子组仅有一个元素为止。

例如一个数组:

6, 3, 1, 9, 2, 7, 4, 5

首先分成两组:

[6, 3, 1, 9] [2, 7, 4, 5]

对于左边,再次分组:

[6, 3] [1, 9]

再对左边分组:

[6] [3]

因为各自仅包含一个元素了,这一个元素组成的字数组就是有序的了。接下来向上递归合并:

[6] [3] -> [3, 6]
[1] [9] -> [1, 9]
[3, 6] [1, 9] -> [1, 3, 6, 9]

对于右半边子数组同样:

[2, 4, 5, 7]

最后再合并:

[1, 3, 6, 9] [2, 4, 5, 7] -> [1, 2, 3, 4, 5, 7, 9]

分组的过程比较简单,直接将数组拆成两部分即可。

合并时,需要交替比较两个数组间的元素,由于合并时,两个子数组均已经排序好了,因此两个数组从头至尾遍历一次即可。
从一个数组A开始,当其中元素开始大于另一数组B的元素时,就停止遍历A,开始遍历B中的元素,当B元素开始比A中的大时,反之继续。

[(1), 3, 6, 9] [<2>, 4, 5, 7] -> [1, _, _, _, _, _, _, _]
[1, <3>, 6, 9] [(2), 4, 5, 7] -> [1, 2, _, _, _, _, _, _]
[1, (3), 6, 9] [2, <4>, 5, 7] -> [1, 2, 3, _, _, _, _, _]
[1, 3, <6>, 9] [2, (4), 5, 7] -> [1, 2, 3, 4, _, _, _, _]
[1, 3, <6>, 9] [2, 4, (5), 7] -> [1, 2, 3, 4, 5, _, _, _]
[1, 3, (6), 9] [2, 4, 5, <7>] -> [1, 2, 3, 4, 5, 6, _, _]
[1, 3, 6, <9>] [2, 4, 5, (7)] -> [1, 2, 3, 4, 5, 6, 7, _]
[1, 3, 6, <9>] [2, 4, 5, 7]() -> [1, 2, 3, 4, 5, 6, 7, 9]

以下是Java版本代码:

package com.example.mergesort;

public class MergeSort {

    /*
     * 顶层的接口,用于外部调用
     * */
    public static void sortA(int[] arrayToSort) {

        int leftIndexStart;
        int leftIndexEnd;
        int rightIndexStart;
        int rightIndexEnd;

        //  在本例中,顶层接口不做数组大小判断了。在此直接获取各下标参数
        int midIndex = arrayToSort.length / 2;
        leftIndexStart = 0;
        leftIndexEnd = midIndex;
        rightIndexStart = midIndex + 1;
        rightIndexEnd = arrayToSort.length - 1;

        // 声明一个与原数组一样大小的空组,用于合并时存储有序数据
        int[] tmp = new int[arrayToSort.length];

        // 递归调用,对左侧子数组继续分组
        sortPart(arrayToSort, tmp, leftIndexStart, leftIndexEnd);
        // 递归调用,对右侧子数组继续分组
        sortPart(arrayToSort, tmp, rightIndexStart, rightIndexEnd);

        // 合并左右两个子组
        merge(arrayToSort, tmp, leftIndexStart, leftIndexEnd, rightIndexStart, rightIndexEnd);
    }

    /*
     * 接收临时存储数组及上下界,用于内部递归调用
     * */
    public static void sortPart(int[] array, int[] tmp, int left, int right) {
        int leftIndexStart;
        int leftIndexEnd;
        int rightIndexStart;
        int rightIndexEnd;

        // 当子数组长度小于1时,直接返回
        if ((right - left) < 1) {
            return;
        }

        // 获取各下标参数
        int midIndex = (right + left) / 2;
        leftIndexStart = left;
        leftIndexEnd = midIndex;
        rightIndexStart = midIndex + 1;
        rightIndexEnd = right;

        // 继续对左侧分组
        sortPart(array, tmp, leftIndexStart, leftIndexEnd);
        // 继续对右侧分组
        sortPart(array, tmp, rightIndexStart, rightIndexEnd);

        // 合并两个子组
        merge(array, tmp, leftIndexStart, leftIndexEnd, rightIndexStart, rightIndexEnd);
    }

    /*
     * 合并函数,使用了唯一的与原数组相同大小的空组。
     * 由于所有合并步骤均在相对应的下标范围操作,因此元素并不会产生重叠。
     * 最后的tmp也即为排序好的数组的拷贝。
     * */
    public static void merge(int[] array, int[] tmp, int leftStart, int leftEnd, int rightStart, int rightEnd) {
        int leftIndex = leftStart;
        int rightIndex = rightStart;
        int tmpIndex = leftStart;

        // 交替遍历左右两组,并改变相应下标。
        while(leftIndex <= leftEnd && rightIndex <= rightEnd) {
            if (array[leftIndex] < array[rightIndex]) {
                tmp[tmpIndex] = array[leftIndex];
                tmpIndex++;
                leftIndex++;
            } else {
                tmp[tmpIndex] = array[rightIndex];
                tmpIndex++;
                rightIndex++;
            }

        }

        // 交替遍历后,有可能一侧剩余下不定数量元素,继续将剩余元素填充进tmp
        while(leftIndex <= leftEnd) {
            tmp[tmpIndex] = array[leftIndex];
            tmpIndex++;
            leftIndex++;
        }

        // 交替遍历后,有可能一侧剩余下不定数量元素,继续将剩余元素填充进tmp
        while(rightIndex <= rightEnd) {
            tmp[tmpIndex] = array[rightIndex];
            tmpIndex++;
            rightIndex++;
        }

        // 将tmp中按顺序填充好的元素覆盖到原数组相应元素上
        for(int i = leftStart; i <= rightEnd; i ++) {
            array[i] = tmp[i];
        }
    }

    public static void main(String[] args) {
        int[] a = { 13, 12, 11, 23, 9, 8, 7, 6, 5, 4, 5, 2, 1 };
        MergeSort.sortA(a);

        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] + ", ");
        }
        System.out.println();
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值