插入排序与希尔排序

首先我们引入一张思维导图,并且以后的排序博客我将以这位博主的博文为基础,进行代码自我实现,简单介绍以及自我理解。
链接:cnblogs.com/cndarren/p/11787368.html。
在这里插入图片描述

这篇博客主要介绍简单插入排序和希尔排序的思想以及自己的实现。

简单插入排序

简单插入排序的思想非常容易理解,就是打扑克时的调整排序。

假如手中有一副牌【1,2,3,1,4,5,2,J,K,3】
最后我们需要把牌调整为从大到小的顺序。

所以我们的操作都是把 1 放到最前面,把 2 放到 第一个2 的后面,再把3放到第一个3的后面,也就是直接插入到某个位置.

我们模仿一下把1放到最前面的操作。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
我们来抽象扑克调整的思路,从任意的一个位置( I )开始从前向后找(从 0 到 I 的位置),找到比它大的位置( J )。先让 J 到 I - 1 的这段牌向后移动,然后把,I位置的这张牌,插入到 J 位置(因为J位置的牌向后移动了一个单位,所以 J 位置空缺)。

那么我们来实现一下。

 public static int[] InserSort(int[] arr){
      if(arr.length < 2 || arr == null){
            return arr;
        }
        for(int i = 1;i < arr.length;i++) { //控制躺数,从第二张牌(也就是1号下标)开始以边遍历到最后
             int temp = arr[i]; //先保存当前下标的值,从前向后遍历到当前位置,
                                // 找到第一个比自己大的位置,先让目标位置 到 当前位置-1的数字全部向后移动一个单位,再放入temp;
            int j = 0;
            while( j < i ){
                if(arr[j] > temp){
                    break;           //当我们找到目标位置的时候我们跳出处理。
                }
                j++;
            }
            if(j != i){ //有可能遍历到当前下标之后没有找到目标位置,也就是没有找到比自己大的数,i之前的序列全部有序,并且 i 比 i-1 大
                for(int x = i ;x > j ;x--){
                    arr[x] = arr[x-1];   //j 下标到 i-1 下标全部向后移动一个单位。
                }
                arr[j] = temp; //在目标位置放入temp
            }
        }

        return arr;
    }

时间复杂度:O(n^2)

空间复杂度:O(1)

稳定性:稳定

优化
我们可以发现无论是最好情况还是最坏情况,我们都是从前向后遍历,导致我们的时间复杂度一直是O(n^2);那么我们能不能把 移数字和放数字合在一起呢?既然当前位置的前面的数字已经有序,我找到比自己小的那个位置的后一个插入,不就好了,如果从后向前找,那么如果 前一个数字就比自己小,自己不就可以不用移动了,那么针对我们的最优序列就可以这么去优化了!

优化实现:

    public  static int[] InsertSort1(int[] arr){
         if(arr.length < 2 || arr == null){
            return arr;
        }
        for(int i = 1 ; i < arr.length ; i++) { //控制趟数。从第二个数字开始插入排序
            int temp = arr[i];
            int j = i;
            while( j > 0 && arr[j-1] > temp){  //从当前位置开始向前寻找
                arr[j] = arr[j-1];
                j--;          //如果比自己大,那就让前一个数字覆盖自己(向后移动)
                }
            if(j != i){       //先判断一下如果目标位置与开始位置不同就证明找到了
            arr[j] = temp;    //找到自己比小的位置了,在其后直接放入temp
            }
        }
        return arr;
    }

如此一来,我们的最优情况下时间复杂度就可以降低置 O(n) 。

由此,我们可以看出插入排序的特性,越有序的序列排序越快

额外知识:Java底层排序运用的是原始归并排序,以及基于多路归并排序实现的TimSort,还有双轴快排,在使用双轴快排时,还是使用到了双插排序,也就是两个数字进行插入排序,算的上是插入排序的一种优化。
思想如下:
在数组中选择两个数,一个数大一个数小,先讲小的数进行插入排序,小的数插入完成后,大的数只需要从小的数插入的位置之后开始寻找插入的位置即可。

在TimSort中还运用到了二分插入排序
二分插入排序顾名思义,在插入时运用到了二分查找的思想,原始的插入排序的思想是,从当前位置向前一个一个寻找比自己小第一个位置,然后插入,而二分插入的思想值直接对当前位置之前的有序序列进行二分查找,这样找位置的时间复杂度会从 O(n)降低到 O(log n) 级别。

希尔排序

希尔排序是基于插入排序实现的,由于插入排序的特新那个,在希尔排序中引入了 gap 这个参数,他将我们的长数组利用 gap 这一间隔,分为了几个小数组,对这些小数组进行直接插入排序后,此时序列就相对变得有序一些,再将gap变小,再进行插入排序。重复操作,直到gap为1;

额外知识:gap的取值,我们可以发现希尔排序的效率实际上就是取决于gap的取值,希尔排序最初是,arr.length 不断的除以2,进行划分,但是后来人们在不断的尝试中发现了效率更高的序列,我们以Knuth序列为例,在后续我们就加以实现,还有其他各式各样的序列,大家请自行尝试。

图示:
在这里插入图片描述
我们简单的用代码实现一下

 	public  static int[] shellSort(int[] arr){
 	     if(arr.length < 2 || arr == null){
            return arr;
        }
      
       //使用kunth序列进行间隔划分
        int  h = 1;
        while(h < arr.length/3){
            h = 3*h +1;
        }
        
      for(int gap = h ; gap > 0 ; gap = (gap-1)/3){
          shell(arr,gap);
      }
      return arr;
    }
	public  static int[] shell(int[] arr,int gap){
	    for(int i = gap;i < arr.length;i+= gap){   //由于每一个被分割的序列的第一个数字不需要排序,所以从0 + gap下标下开始排序
	        int temp = arr[i];                  //进行直接插入排序
	        int j;
	        for( j = i  ; j > gap-1 && arr[j-gap] < temp;j -= gap){
	            arr[j] = arr[j-gap];
	        }
	        if(i != j ){
	            arr[j] = temp;
	        }
	
	    }
	    return  arr;
	}
	}


时间复杂度:O(n^1.3) - O(n^1.5)
希尔排序的时间复杂度至今没有一个极其精确的值,但是书上给出的数据平均时间复杂度就是 O(n^1.3)
空间复杂度:O(1)
稳定性:不稳定

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值