• 归并排序


    本文参考:图解排序算法(四)之归并排序,大家看这篇文章就够了,不用看我的文章,我的文章没他写的好

    概述

      前几篇文章,写了快速排序,希尔排序,这两种排序算法都是使用某种方法把数组分成几个部分,然后在每个小的部分进行排序,之后再对整体进行排序,不过快速排序和希尔排序的分段都不彻底,那什么是彻底的呢?就是直接把数组的每个元素分成一组,而一个元素自然是有序的,然后再两两合并,最后就可以实现整个数组的排序。

    核心思想

      其实在概述中已经说了,归并排序就是先分,再合,在合并的时候对要合并的两个小片段进行排序,时间复杂度为(O(nlogn)),还不错,而且很稳定,不像快速排序和希尔排序,在最坏的情况都要O(n2),好啦,下面通过一个例子来讲解什么是归并排序。

    举例:2,4,1,3,6,5

    要求:使用归并排序对数组进行排序

    拆分过程如下

    1. 将数组分成两组【2,4,1】,【3,6,5】
    2. 再分别对两组数据进行拆分,分为四组【2】,【4,1】      【3】,【6,5】
    3. 继续分,一定要分成一个一个的元素,分为【2】  【4】,【1】  【3】          【6】,【5】

    以上的过程用二叉树表示就是如下:

      

     合并过程如下

    从树的最底层开始合并,在合并的时候比较4和1的大小,合并成【1,4】,之后再把【2】和【1,4】合并成【1,2,4】,这是合成左子树的,然后把右子树的也合并了,最后在合并左右子树的结果。。。就这样下去就可以实现排序

    上面哔哔叭叭哔哔叭叭那么多,看的是不是dan疼(女生请不要对号入座^_^,估计也没有女生)

    代码实现

      这个代码实现拆分的过程采用递归做的,代码说复杂也复杂说简单也简单,反正我是一开始是自己想,想了半天想了一个思路,后来否定了,看了看大神的文章,发现没有想错,后面的都是自己实现的,也许不是最好的方式,大家姑且看吧

    /**
     * @author: steve
     * @date: 2020/7/7 11:47
     * @description: 归并排序
     */
    public class MergeSort {
    
        public static int array[] = {72, 6, 57, 88, 60, 42, 83, 73, 48, 8, 1};
    //    public static int array[] = {6, 75,8,14};
        //临时数组,用于作为两个有序数组合并的中间数组
        public static int temp[] = new int[array.length];
    
        /***
        *@Description 递归实现把数组分成小块
        *@Param [start, end]
        *@Return void
        *@Author steve
        */
        public static void mergeSort(int start, int end){
            int mid = (end + start)/2;
            //下面这个打印可以看出来自己递归有没有问题,大家自己写的时候也可以这样打印出来
    //        System.out.println(start+"-------"+end);
            if (end > start){
                //左边归并排序
                mergeSort(start,mid);
                //右边归并排序
                mergeSort(mid+1,end);
            }
            merge(start,mid,end);
        }
    
        /***
        *@Description 执行合并的方法,自己想的一个,可能有些复杂
        *@Param [start, mid, end]
        *@Return void
        *@Author steve
        */
        public static void merge(int start, int mid, int end){
            
    //        System.out.println(start+"==="+mid+"==="+end);
            //游标是为了控制插入临时数组的位置
            int cursor = start;
            int i = start;
            int j = mid+1;
            while(i <= mid || j <= end){
                if (i <= mid && j <= end){
                    if (array[i] <= array[j]){
                        temp[cursor] = array[i];
                        i++;
                        cursor++;
                    }else {
                        temp[cursor] = array[j];
                        j++;
                        cursor++;
                    }
                }else {
                    //这个else的作用就是当要合并的两部分,其中一部分已经全部插入到临时数组,另一部分还有剩余的
                    //直接将剩余部分插入临时数组,不用比较
                    if (i <= mid){
                        temp[cursor] = array[i];
                        i++;
                        cursor++;
                    }
                    if (j <= end){
                        temp[cursor] = array[j];
                        j++;
                        cursor++;
                    }
                }
    
            }
            System.arraycopy(temp,start,array,start,end-start+1);
    //        for (int k = 0; k < array.length; k++) {
    //            System.out.println(array[k]);
    //        }
        }
    
        /***
        *@Description 测试System.arraycopy()函数的功能
        *@Param []
        *@Return void
        *@Author steve
        */
        public static void arraycopy(){
            int test1[] = {1,2,3};
            int test2[] = {2,3,4};
            System.arraycopy(test2,1,test1,1,1);
            for (int i = 0; i < test1.length; i++) {
                System.out.println(test1[i]);
            }
        }
    
    
        public static void main(String[] args) {
            mergeSort(0,array.length-1);
            //单独测试merge的功能有没有问题
    ////        merge(0,0,1);
            for (int i = 0; i < array.length; i++) {
                System.out.println(array[i]);
            }
    //        arraycopy();
        }
    
    }

    总结

      在文章的开头,我写了参考的文章,在那篇文章中,在文章结尾作者说java的array中的sort方法使用的TimSort就是一种优化版本的归并排序,我之前正好看过这个方法,当时并不知道什么是归并排序,当时看到sort排序的时候感觉作者Tim Peter真牛逼,这也可以想到,现在看来,其实他就是在归并排序的基础上做了点优化而已。具体优化的地方就是如下几点:

    1.他没有把数组拆分成一个一个的元素,而拆成最小的单元为32,如果拆分的片段的长度小于32,采用的是二分插入排序,如果不了解这个算法的,可以参考我另一篇文章:插入排序

    2.寻找自然升序或者降序片段,防止出现了逆序的,然后再使用插入排序效率很低

    3.在合并两个片段的时候会互相寻找合适的起始点和结束点

    (。。。)

    并没有列举完,里面还有几个优化的点,不好用文字叙述出来,反正在我看来,只要能优化的点,Tim Peter都捣鼓了一下

    具体参考我的另一篇文章:java8中List中sort方法解析

  • 相关阅读:
    调试IPV6
    [super class]和[self class]
    Django2 + python3 上传图片
    django2 + python3 显示静态文件中的图片
    机器工厂——贪心
    Handstand 2——AT(先打表)
    Low Elements--AT
    Double Factorial——AT
    Brick Break——AT
    变音量——动态规划
  • 原文地址:https://www.cnblogs.com/gunduzi/p/13263055.html
Copyright © 2020-2023  润新知