插入排序(常见排序)【直接插入+希尔】

目录

一、 直接插入排序 

1. 思想

2.思路

3. 做法

4. 代码实现

5. 复杂度分析

最坏:O(N^2) 【逆序 】

最好:O(N)    【顺序】

平均:O(N^2)

6.稳定性:稳定

7. 结论:越有序越快 (直接插入排序)

8.优化 ----->  希尔排序

​编辑

二、 希尔排序  

1.思想

1.gap的选择

2.代码

1.代码解释

3.复杂度分析

4.稳定性分析:不稳定


例子均为从小到大排序

1. 直接插入排序

2. 希尔排序       【分组后的直接插入排序】

越有序越快,适用于比较有序的数据

一、 直接插入排序 

1. 思想

把一个数字插入到一组已经排好序的数组里的合适位置(从后往前插)

eg:打牌

2.思路

把数据分为有序区间和无序区间,无序区间里的数依次插入到有序区间的合适位置

有序区间 : [0, i)                                   // i:要插入的数
无序区间 : [i, array.length)

假设第一个元素是有序的

从第二个元素开始,依次和前面的元素进行比较

找到合适的位置,就进行插入

3. 做法

将待插数存入tmp,从有序区间最后一个数开始依次往前比较,若有序区间的数>tmp,则有序区间的数后移一位,直到将待插入的数插入到合适位置

(1)数组的第1个元素默认算是排好序的,所以从数组的第2个元素开始,把它当做要插入的数字 

(2)此时,第1个和第2个元素组成一个排好序的数组,把第3个元素插入到合适的位置,使成为一组排好序的数组(含3个元素)

(3)第4,5,6...个元素一次类推,直到整个数组都成为一个排好序的数组

4. 代码实现

/**
     * 直接插入排序
     * @param arr
     */
    public void insertSort(int[] arr){
        //从第2个元素开始插入(i=1)
        //有序区间: [0, i)
        //无序区间: [i, array.length)
        for(int i=1;i<arr.length;i++){
            int tmp=arr[i];     //tmp临时存放要插入的元素(无序区间的第一个数)
            int j=i-1;
            for(;j>=0;j--){
                if(arr[j]>tmp){
                    arr[j+1]=arr[j];
                }else{
                    break;
                }
            }
            //插入到了合适的位置
            arr[j+1]=tmp;
        }
    }

5. 复杂度分析

最坏:O(N^2) 【逆序 】

逆序下总共比较:1+2+3+......+n ,即等差数列求和【(a1+an)*n / 2】——>  N^2

最好:O(N)    【顺序】

顺序下,1+1+1+......+1=N

平均:O(N^2)

6.稳定性:稳定

arr[j]>tmp时才后移,所以等于(arr[j]==tmp)的情况下,arr[j]并不后移,tmp相对于arr[j]来说,位置并没有发生改变(还是在arr[j]的后面)

注:稳定的排序可以改为不稳定的,但是不稳定的排序一定不能通过修改代码变为稳定的

7. 结论:越有序越快 (直接插入排序)

针对该结论,我们进行优化

8.优化 ----->  希尔排序

怎样让数组变得越来越有序?  分组

1.思想

分组,组数gap,开始gap比较大,后面越来越小,直到gap=1

eg: 【每组采用直接插入排序,越有序越快】

      gap=4时,在组内,数组非常无序,但是组内的数据少,插入排序快

      gap=2时,。。。。。。。。。。。。。。。。。。。。。(整个数组有序了不少)

      gap=1时,组内数据非常多,但数据都比较趋于有序,插入排序也快

这样,就使得插入排序越来越快,达到了优化的目的

大佬的思想(希尔排序):跳跃式分组,让小的数据尽量靠前,大的尽量靠后。(假如1和100是第1组的2个数,大佬的方法能让100尽量靠后;而我的方法会让100处在数组第2个位置,不太好)

二、 希尔排序  

1.思想

先选定一个整数gap,把待排序数组分成gap个组,并对每一组内的记录进行排序。然后,gap缩小,重复上述分组和排序的工作。当到达gap=1 时,所有数据在同一组内排好序。
(以上每次操作均使用的是直接插入排序)

1.gap的分析(希尔排序思想分析)

为了尽可能有序,在直接插入排序的基础上进行跳跃式分组,组数gap。

随着gap减少,组数在减少,每组数据在增多。

组数多,每组数据少,排序快;组数少,每组数据多,但数据趋于有序,排序快。

最后把整体看做一组时,数据已经趋于有序了(不是完全有序),此时进行直接插入排序,速度会更快

2.gap的选择

①gap=n/2   gap=gap/2 ...... 直到gap=1      (常用)

②gap=gap/3 + 1 .........

...........gap的取法有很多种

2.代码

/**
     * 希尔排序
     * @param arr
     */
    public void shellSort(int[] arr){
        int gap=arr.length;
        while (gap>1){
            gap/=2;
            shell(arr,gap);
        }
    }

    private void shell(int[] arr, int gap) {
    //i=0+gap:第一组的第2个数       // i++ :这里指多个组交替进行组内排序
        for(int i=gap;i<arr.length;i++){
            int tmp=arr[i];
            //和组内前一个数依次往前比较
            int j=i-gap;
            for(;j>=0;j-=gap){
                if(arr[j]>tmp){
                    //组内后移
                    arr[j+gap]=arr[j];
                }else{
                    break;
                }
            }
            arr[j+gap]=tmp;
        }
    }

1.代码解释

①i++ : 这里指多个组交替进行组内排序,确保每个组都进行了组内排序

②for(int i=gap;...) : 这里的【 i=gap】其实是【 i=0+gap】 ,表示从第1组的第2个元素开始 ; 也可改为【 i=1】:因为i++确保了每个组都进行了组内排序,所以从第2个元素开始也OK

③和直接插入排序几乎一致,除了组内比较不同【( j-- / j+1  )    ——>(j-gap / j+gap)】

3.复杂度分析

空间复杂度:0(1)

时间复杂度:

最好:0(N)  【顺序 】

最坏:0(N^2)   较难构造

平均:0(N^1.3 --- N^1.5)    众多大佬算出来的(背就完事了)

4.稳定性分析:不稳定

若相同的数在不同的分组,最后gap=1时的直接插入排序中,很有可能两个相等数据最后的相对位置 和 一开始没经历任何排序时的相对位置 不一样

5.个人意见

考的不多。但是要知道这个希尔排序的思想!做了什么?为什么这样做?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值