插入 & 希尔 & 归并 算法详解

我们常见的排序算法有:(选择、冒泡、快速)、(插入、希尔、归并)算法,在这里给大家讲解一下插入、希尔、以及归并排序,这六个算法都是按照我写的顺序依次递进的关系,他们的时间复杂度是越来越低的,这篇文章先讲解一下,插入,希尔以及归并排序算法

插入排序

所谓插入排序体现在一个插入上面,他的思想就是将需要排序的数据拿出来跟前面已经排好序的数据进行比较,如果满足大小,就将此数据插入到有序数据中去

相信大家都打过扑克:你手中的牌就是你已经理好的牌,相当于有序的数组,而当你摸起来下一张的时候,你就会用这张牌去跟手中的牌比大小,比如你摸一个6,你只需要跟手中有序的牌进行对比,就能知道他应该插入到什么位置

下面用数组举一个例子:
首先我们看到有一个需要排序的数组,插入排序是往有序数组中插入,我们就默认数组第一个数是一个有序数组。相当于我们是将: { 5 } { 2,4,1, 3,6}这两个数组进行排序。
然后我们会将第二个无序数组中的值依次拿出来进行比较:
step1: 6>5 {5,6}
step2: 3<6 ; 3<5 {3,5,6}
step3: 1<6;1<5;1<3; {1,3,5,6}
step4: 4<6;4<5;4>3;{1,3,4,5,6}
在这里插入图片描述
代码实现:其中代码做了一些优化 当我们第二个无序数组需要往第一个有序中插入的时候,从末尾开始,这样我们就解决的数组的扩容问题

 public static void insertSort(int []nums){
        long start=System.nanoTime();
       int n=nums.length;
        //从第二位开始遍历  默认0-i是排好序的 需要将i-(n-1)的数据进行排序
       for (int right=1;right<n;right++){
           //把当前右指针的值存储起来
           int data =nums[right];
           //左指针满足以下条件:左指针在右指针前面 ,左指针的值必须要比右指针的值小
           int left=right-1;
           //从0-i数组的最后以为进行对比 数组末尾进行处理 解决了数据迁移的扩容问题
           for (;left>=0;left--){
               //如果左指针的数大于右指针 就不满足条件 需要进行数据的移动
               if (nums[left]>data){
                   //将当前左指针的数据往后移动一位 然后用data(当前右指针指向的值跟当前作指针的前一位进行对比)
                   nums[left+1]=nums[left];
               }else {
                   break;
               }

           }
           //如果左指针的值小于右指针 说明是满足排序大小条件 将当前left指针的下一坐标数组值改为data
           nums[left+1]=data;
       }
        System.out.println("插入排序:"+(System.nanoTime()-start));

    }

希尔排序

希尔排序其实是插入排序的一个改进版,希尔排序把数据按照下标的一定增量进行分组,然后每组在进行插入排序,当增量为1是,那所有的数据也已经排好序了。
如图所示:
增量一般取数组长度%2,然后比较,比较的时候是数据下标加上增量就是比较的值,一直递归循环下去,直到增量数据为1,两两比较完成就排好序了;
在这里插入图片描述你可能还不知道这为什么是改进版的插入排序,我们通过插入代码分析可以知道插入算法最好的情况下:时间复杂度为O(n) 就是第二个循环全部都break 就是右指针的值都比左指针的值都大,这样就不用交换位置,。 最坏的情况是O(n²),每一个都需要交换,而希尔排序就是通过增量的方法,尽量的增加了break的机会,减少时间复杂度

  public static void Shell(int []nums) {
        long start = System.nanoTime();
        int n = nums.length;
        //相对于插入排序 就是加了增量这个操作 add为增量
        for (int add = n >> 1; add >= 1; add = add >> 1) {
            for (int right = add; right < n; right++) {
                int data = nums[right];
                int left = right - add;
                for (; left >= 0; left -= add) {
                    if (nums[left] > data) {
                        nums[left + add] = nums[left];
                    } else {
                        break;
                    }

                }
                //如果左指针的值小于右指针 说明是满足排序大小条件 将当前left指针的下一坐标数组值改为data
                nums[left + add] = data;
            }

        }
        System.out.println("希尔排序:" + (System.nanoTime() - start));
        System.out.println(Arrays.toString(nums));
    }

归并排序
归并排序是建立在希尔排序的基础上,理论上他比前两种排序的时间复杂度都要低,归并排序是利用了递归和分治的算法思想,将数组拆分成单个的数据然后进行排序回溯,如图所示:
我们将数组进行分割,分割成单个数据,在进行排序,排完序再返回,最终就得到排完序的数组
在这里插入图片描述
代码实现:

  public static void merge(int[] nums){
        long sa=System.nanoTime();
        // 使用递归将数据进行分组
        int left=0;
        int right=nums.length-1;
        grouping(nums,left,right); //分组 将数组一分为二 知道分成单独的数据项
        System.out.println("归并排序:"+(System.nanoTime()-sa));

    }

    public static void grouping(int[] data,int left,int right){
        long sa=System.nanoTime();
        if (left<right){
            int mid=(left+right)>>1;
            //将数组进行拆分成两个部分 利用尾递归
            grouping(data,left,mid); //处理前半部分
            grouping(data,mid+1,right); //处理后半部分
            //将数据分为单独的数据项  然后对比之后进行排序
            sort(data,left,mid,right);//排序处理
        }
        System.out.println("归并排序:"+(System.nanoTime()-sa));
    }
    public static void sort(int[] data,int left,int mid,int right){
        int[] temp=new int[data.length];// 利用一个空数组装排好序的数组
        int point1=left; //第一个数组起始位置
        int point2=mid+1; //第二个数组起始位置
        int loc=left; //空数组数据安放的位置
        while (point1<=mid && point2<=right){  //将两个数组数据拿出来依次比较 小的放进数组 在比较下一个
            if (data[point1]<data[point2]){
                temp[loc++]=data[point1++];
            }else {
                temp[loc++]=data[point2++];
            }
        }
        //比较完之后 剩下最后一个就是最大的数据 放到数组的最后
        while (point1<=mid){
            temp[loc++]=data[point1 ++];
        }
        while (point2<=right){
            temp[loc ++]=data[point2 ++];
        }

        for (int i = left; i <=right ; i++) {
            data[i]=temp[i];
        }
    }

归并排序的时间复杂度是0(n) ,但是需要利用额外的空间,对比之下,快速排序的时间复杂度与之相同,还不用消耗额外的空间

总结一下

稳定性:相同的两个数排完序之后相对位置不变
在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值