关于“希尔排序”的一点思考(参考浙大陈越老师的教学视频)

最近复习了一下七大排序算法,到 “希尔排序” 这里时,看了一下浙大的数据结构视频(网易云课堂)。

看了几分钟就开始尝试着自己用java实现(先不看视频中给出的源代码),结果发现一个有意思的过程,现在记录一下。

要理解 “希尔排序” ,得先理解 “插入排序”,因为前者相当于后者的进阶。

1.插入排序

 插入排序的排序策略很简单,将数组分为两堆:已排序 + 未排序,然后将未排序的元素,一个个的插入到前面已排序的数组中。

// 插入排序
public static void insertSort(int[] nums) {
    int size = nums.length;
	
    for(int p=1; p<size; p++) {
		
        int insertNum = nums[p]; //待插入的那个元素
        int insertIndex = p;
		
        while(insertIndex>=1 && nums[insertIndex-1]>insertNum) {
            nums[insertIndex] = nums[insertIndex-1];
            insertIndex--;
        }
        nums[insertIndex] = insertNum;
    }
}

2.希尔排序

 首先,按照陈越老师的那个例子,自己手动写了一遍,发现跟给出的代码不一样, 但结果也可行。

先看视频中的例子:排序下面的数组。

value81941196123517952858417515
index0123456789101112

 

 

 

操作方法1: 

视频中,先用5间隔排序:

  • 待排序的序列为(用下标表示):(0,5,10),(1,6,11),(2,7,12),(3,8),(4,9)
  • 每一个上面的序列,都 分别用插入排序

经过5间隔的操作,结果如下图:

value35171128124175159658819495
index0123456789101112

 

 

 

然后用3间隔排序:

  • 待排序的序列为(用下标表示):(0,3,6,9,12),(1,4,7,10),(2,5,8,11)
  • 上面的三个序列,都 分别用插入排序

经过3间隔的操作,结果如下图:

value28121135154158179475819695
index0123456789101112

 

 

 

最后用1间隔排序:

  • 待排序的序列为(用下标表示):(0,1,2,3,4,5,6,7,8,9,10,11,12)
  • 上面的序列,用插入排序

经过1间隔的操作,结果如下图:

value11121517283541587581949596
index0123456789101112

 

 

 

操作完毕!

然后我就照着上面的操作顺序,尝试着写代码:

代码1:

// 希尔排序1
public static void shellSort1(int[] nums) {
    int size = nums.length;
	
    //增量序列有很多种,此次我们选择(size/2, size/4, size/8,  ...  ,1)
    for(int D=size/2; D>=1; D/=2) {
        for(int s=0; s<D; s++) {  // 变量s,代表每次 待“插入排序”序列的起始索引位置
			

            //到此处,就确定了一个待“插入排序序列” (s, s+D, s+2D, .....),下面对其进行插入排序
            for(int p=s+D; p<=size-1; p+=D) {
                int insertNum = nums[p];  //待插入的那个元素
                int insertIndex = p;
                while(insertIndex>=s+D && nums[insertIndex-D]>insertNum) {
                    nums[insertIndex] = nums[insertIndex-D];
                    insertIndex -= D;
                }
                nums[insertIndex] = insertNum;
            }


        }
    }
}

运行了一下,也没发现有什么问题,可以将上面的例子正确输出。但是总感觉怪怪的,竟然用了四层循环嵌套,会不会影响效率啊?然后看了看源代码,照着源码的思路写下了:

 

操作方法2:

// 希尔排序2
public static void shellSort2(int[] nums) {
    int size = nums.length;
	
    //增量序列有很多种,此次我们选择(size/2, size/4, size/8,  ...  ,1)
    for(int D=size/2; D>=1; D/=2) {
        for(int p=D; p<size; p++) { 
			
            int insertNum = nums[p];  //待插入的那个元素
            int insertIndex	= p;
            while(insertIndex>=D && nums[insertIndex-D]>insertNum) {
                nums[insertIndex] = nums[insertIndex-D];
                insertIndex -= D;
            }
            nums[insertIndex] = insertNum;
        }
    }
}

 尼玛,怎么只有三层循环嵌套就可以了?

仔细想了想,发现原因如下:

仍以最上面的例子为例,三层循环嵌套的那段代码,操作实际上是:

  • 5间隔排序,操作的序列下标为:(下面的数字均是指下标)

(0,5)仅将元素5往前插,仅做一次插入

(1,6)仅将元素6往前插,仅做一次插入

(2,7)仅将元素7往前插,仅做一次插入

(3,8)仅将元素8往前插,仅做一次插入

(4,9)仅将元素9往前插,仅做一次插入

(5,10)仅将元素10往前插,仅做一次插入

(1,6,11)仅将元素11往前插,仅做一次插入,序列(1,6)已在前面的过程排序好

(2,7,12)仅将元素12往前插,仅做一次插入,序列(2,7)已在前面的过程排序好

  • 3间隔排序,操作的序列下标为:(下面的数字均是指下标)

(0,3)仅将元素3往前插,仅做一次插入

(1,4)仅将元素4往前插,仅做一次插入

(2,5)仅将元素5往前插,仅做一次插入

(0,3,6)仅将元素6往前插,仅做一次插入,序列(0,3)已在前面的过程排序好

(1,4,7)仅将元素7往前插,仅做一次插入,序列(1,4)已在前面的过程排序好

(2,5,8)仅将元素8往前插,仅做一次插入,序列(2,5)已在前面的过程排序好

(0,3,6,9)仅将元素9往前插,仅做一次插入,序列(0,3,6)已在前面的过程排序好

(1,4,7,10)仅将元素10往前插,仅做一次插入,序列(1,4,7)已在前面的过程排序好

(2,5,8,11)仅将元素11往前插,仅做一次插入,序列(2,5,8)已在前面的过程排序好

(0,3,6,9,12)仅将元素12往前插,仅做一次插入,序列(0,3,6,9)已在前面的过程排序好

  • 1间隔排序,操作的序列下标为:(下面的数字均是指下标)

(0,1)仅将元素1往前插,仅做一次插入

(0,1,2)仅将元素2往前插,仅做一次插入

(0,1,2,3)仅将元素3往前插,仅做一次插入

(0,1,2,3,4)仅将元素4往前插,仅做一次插入

(0,1,2,3,4,5)仅将元素5往前插,仅做一次插入

(0,1,2,3,4,5,6)仅将元素6往前插,仅做一次插入

(0,1,2,3,4,5,6,7)仅将元素7往前插,仅做一次插入

(0,1,2,3,4,5,6,7,8)仅将元素8往前插,仅做一次插入

(0,1,2,3,4,5,6,7,8,9)仅将元素9往前插,仅做一次插入

(0,1,2,3,4,5,6,7,8,9,10)仅将元素10往前插,仅做一次插入

(0,1,2,3,4,5,6,7,8,9,10,11)仅将元素11往前插,仅做一次插入

(0,1,2,3,4,5,6,7,8,9,10,11,12)仅将元素12往前插,仅做一次插入

完毕!

3.总结

      

这两段代码(代码1:四层嵌套; 代码2:三层嵌套)起始对应着两种不同的插入方法,本质都是插入排序。

操作方法1:我先确定这些固定间隔的 完整的待排序列(完整的意思就是从头到数组尾部的间隔序列),然后对这些序列进行单独的插入排序。

操作方法2:我不用首先就立马确定一个待排序的序列,而只要知道下一个即将要插入的元素就可以了。在插入过程中,已排序的序列慢慢变多了,变壮大了。而后面未排序的序列,我不考虑。

 

而关于 “希尔排序” 的时间复杂度,这非常复杂的!只要记住最坏的时间复杂度是 O(n2)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值