插入排序
插入排序的思想是将需要排序的数组分成前后两个部分,前面有序,后面随意。前面有序的部分从第一个元素开始,后面的第一个元素依次从右向左比较后插入到适当的位置。
不过这种排序的时间复杂度为O(N^2),复杂度高的原因主要是数组中较大元素移到右边和较小元素移到左边都需要一位一位移动,为了解决这个弊端,就有了希尔排序。
希尔排序
希尔排序是插入排序的升级版,一共分为两步:预排序和普通插入排序。
其中预排序主要是将数组转化成一个“比较有序”的数组,大数在后面,小数在前面。
预排序仍运用了普通插入排序的思想,预排序将数组里的元素分为gap组,组内元素在原数组内的位置差值为gap,将gap组直接插入排序后,整体上大数会到后面,小数会到前面,这极大节省了下一步普通插入排序大数和小数需要移动的位数,整体下来算法的时间复杂度会达到O(N^1.3)左右。
如果gap是3时候的分组:
我们将第一组进行直接插入排序,
不难发现,对第一组排序的代码与直接插入排序的代码几乎一样,(如果将gap替换成1,那这就是普通插入排序)。
我们的目的是让这些组全部进行插入排序,而差异只是首元素在数组中的起始位置不同,所以我们利用循环就可以写出预排序代码
如此我们便写出了希尔排序的预排序代码(初步)
但是gap是多少的时候最合适呢?
不难发现,gap越大,大元素就可以一下子往后移动很多位,小元素可以一下往前面移动很多位,但是预排序结束后的数组就越不接近有序
而如果gap越小,大元素和小元素移动的位数就越少,但是预排序结束后的数组就越接近有序。gap是1时,直接就是有序数组。
为了平衡这个,我们的方法是将gap从大到小,进行多次预排序,然后再进行直接插入排序,而直接插入排序就是gap为1时候的预排序,所以我们得到最终的代码
测试一下:
这里我们直接通过调试观察排序前后的数组:
排序前:
排序后:
如果你觉得三层循环不够优雅,那么可以对其稍作修改
这样的写法是从左到右将大数组里的元素一个个进行了小组内的排序,没了小组的先后顺序
三层循环的写法是一个小组一个小组进行排序
要注意的是,这只是写法的改变,程序运行效率和原来一样
这样希尔排序就优雅地完成了......