Veiking百草园


老狗啃骨头之算法-归并排序

老狗啃骨头   @Veiking   2020-10-29

老狗啃骨头之算法-归并排序

摘要:

归并排序是一种非常典型的分治策略应用排序算法,简而概括:分而排之,合而并之。归并排序,据说是冯·诺伊曼在1945年首次提出。冯·诺伊曼,是现代计算机科学发展史上开天辟地的大佬之一,不单单是计算机领域,这哥们在整个数学、量子力学和经济学中都做出了卓越的贡献,简直超神一般,遥敬大佬:冯先生 long live !

归并排序(Merge Sort)

  归并排序是建立在归并操作上的一种相对高效的排序算法。是一种非常典型的分治策略应用排序算法,简而概括:分而排之,合而并之。
  归并排序,据说是冯·诺伊曼在1945年首次提出。冯·诺伊曼,是现代计算机科学发展史上开天辟地的大佬之一,不单单是计算机领域,这哥们在整个数学、量子力学和经济学中都做出了卓越的贡献,简直超神一般,遥敬大佬:冯先生 long live !

基本思想

  归并排序其核心即是将待排数据样本,按规则逐次分割成更小样本集合,拆至不能再拆,将最小样本合并为有序序列,如此递归回去,从而最终达到整个数据样本排序的操作。
  其基本思想如图所示:


  我们可以看到,图示很直观的展示了归并排序算法的核心。

## 算法描述
1:把长度为n的数据样本分成两个长度为n/2的子样本;
2:逐次对子样本重复1操作,直至不能再分;
3:对子样本做归并操作使其成为有序序列;
4:逐次对子样本重复3操作,直至所有数据归并完成,排序结束。

## 动图演示


代码实现

import java.util.Arrays;

/**
 * @author :Veiking
 * @version :2020年10月15日 
 * 说明 :归并排序算法
 */
public class MergeSort {
    /**
     * 归并排序入口
     * @param arrays
     * @return
     */
    public static int[] sort(int[] arrays) {
        arrays = division(arrays, 0, arrays.length - 1);
        return arrays;
    }

    /**
     * 归并排序之二分

     * @param arrays
     * @param left
     * @param right
     * @return
     */
    public static int[] division(int[] arrays, int left, int right) {
        int center = (left + right) / 2;
        // 二分左右,进入递归,直到left==right,数组即不能再分
        if (left < right) {
            division(arrays, left, center);
            division(arrays, center + 1, right);
        }
        // 当数组二分不能再分,开始归并排序
        merge(arrays, left, center, right);
        return arrays;
    }

    /**
     * 归并排序之归并
     * @param arrays
     * @param left
     * @param center
     * @param right
     */
    public static void merge(int[] arrays, int left, int center, int right) {
        // 用于暂存小组归并排序后的数组的临时数组
        int[] tempArrays = new int[right - left + 1];
        int i = left;
        int j = center + 1;
        // 临时数组的下标
        int index = 0;
        // 同时循环遍历左右两个数组元素,按大小顺序依次插入到临时数组里
        while (i <= center && j <= right) {
            // 若左边数组的元素较小,则插入到临时数组;若右边数组的元素较小,则插入到临时数组。此行为即为归并的典型操作
            if (arrays[i] < arrays[j]) {
                tempArrays[index] = arrays[i];
                i++;
            } else {
                tempArrays[index] = arrays[j];
                j++;
            }
            index++;
        }
        // 左半边数组多余的数据处理,将左半边多余数据直接追加入临时数组
        while (i <= center) {
            tempArrays[index] = arrays[i];
            i++;
            index++;
        }
        // 右半边数组多余的数据处理,将右半边多余数据直接追加入临时数组
        while (j <= right) {
            tempArrays[index] = arrays[j];
            j++;
            index++;
        }
        // 归并完毕,将临时数组中的数据重新放进原数组
        for (int k = 0; k < index; k++) {
            arrays[left + k] = tempArrays[k];
        }
    }

    // 执行
    public static void main(String[] args) {
        int[] input = new int[] { 3, 6, 9, 5, 13, 15, 4, 11, 1, 12, 16, 2, 14, 10, 7, 8 };
        System.out.println("样本初始样本数据:" + Arrays.toString(input));
        int[] output = MergeSort.sort(input);
        System.out.println("归并排序结果数据:" + Arrays.toString(output));
    }
}

算法分析

  选择排序算法的基本性能,归纳如下图:

1、 时间复杂度

  根据上边分析的图示可以看出,归并排序展开看就像是一棵二叉树,它需要遍历的次数就是这个二叉树的深度,且这个跟待排数据元素是否有序而也无关系,统统都要递归分割、逐次归并,根据完全二叉树的深度,可以得出它的时间复杂度是O(nlog2n),最好最坏平均都一样,都是O(nlog2n)

2、 空间复杂度

  由上边的说明分析可以知道,每次元素进行归并排序算法处理的时候,都需要用一个长度为n的临时数组用于保存合并序列,故空间复杂度为O(n)

3、稳定性

  根据归并排序的规则,可以确定的是,无论是数据样本的分割过程还是归并过程,都是临近数据的对比操作,不存在什么不确定性,可以保证等值元素相对位置不发生改变,故归并序也是稳定的算法


老狗啃骨头



慷慨发言

(您提供的信息将用于后续必要的反馈联系,本站会恪守隐私)

潜影拾光

西海之旅

青海湖,很漂亮。

扫码转发

二维码
二维码
二维码
二维码
二维码
二维码

博文标签