希尔排序
本文参考:图解排序算法(二)之希尔排序,大家看这篇文章就够了,不用看我的,我的文章为了给自己做个笔记,方便自己以后看
概述
希尔排序,听名字很奇怪,就是以这个算法的发明人的名字命名的,这个排序算法是对插入排序的一个优化版本,本来以为优化的很好,仔细研究了一下,很失望,优化的并不怎么样,最差的情况时间复杂度依然是O(n2),通过优化增量序列可以使时间复杂度减小,什么是增量序列呢?下面会介绍。
核心思想
由于插入排序在小数据集并且基本有序的数据集上表现较好,如果一个数据集很大并且排序很乱,那插入排序效果很一般,基于以上的原因,希尔排序其实就是针对这两点做的优化。
针对第一点,数据集大时,表现差:希尔排序把大数据集分成一个一个的小段(注意:并不是把一个数组分成多个小数组,逻辑上分的)
针对第二点,数据排序比较乱:希尔排序是先在每一小段上排序,但是整体并不是有序的,然后这样就可以使整个数据集没有那么乱了,再采用插入排序
下面会通过举例的方式介绍希尔排序处理过程:
举例:2,4,3,1,5,7,6,9,8,0
要求:采用希尔排序算法进行排序
实现过程如下:
- 第一步:数据集数据个数为10,第一次把数组分成5组,也就是10/2,结果为:[2,7], [4,6], [3,9], [1,8], [5,0],分成这5组,注意是逻辑分组,不是物理分组
- 第二步:对第一步的分组结果进行排序,结果为2,4,3,1,0,7,6,9,8,5,进过第一轮,5和0发生了交换
- 第三步:将数据集分成2组,也就是5/2(向下取整),分组结果为:[2,3,5,6,8], [4,1,7,9,0]
- 第四步:对第三步的结果排序,结果为:2,0,3,1,5,4,6,7,8,9,红色为第一组的,黑色为第二组的
- 第五步:对整个数据集排序,就是2/2,结果为:[0,1,2,3,4,5,6,7,8,9]
上面的过程中,是先把数组分成5份,之后是2份,之后是1份,大家可能困惑为什么这样分,其实这个分法就是希尔推荐的分法,但是这种并不是最好的分法,后来又有其他人提出了别的方法。其实本文的增量序列为{1,2,4,8,...}这种,但是由于数组长度是10,导致变成了1,2,5,^_^,如果数组长度为16,那就可以变成1,2,4,8这种。
另外两种分法:(参考:希尔排序--简单易懂图解)
Hibbard提出了另一个增量序列{1,3,7,...,2^k-1},这种序列的时间复杂度(最坏情形)为O(n^1.5)
Sedgewick提出了几种增量序列,其最坏情形运行时间为O(n^1.3),其中最好的一个序列是{1,5,19,41,109,...}
上面哔哔叭叭那么多,看的dan疼,看代码吧
代码实现
/** * @author: steve * @date: 2020/7/7 10:13 * @description: 希尔排序 */ public class ShellSort { public static int array[] = {72, 6, 57, 88, 60, 42, 83, 73, 48, 8, 1}; //gap 间隔 public static void shellSort(){ //这里的增量采用希尔增量,所谓增量其实就是你要把数组分成一个部分,这里是一直除于二 //相当于第一次分成5份,第二次分成2份,第三次分成1份,结束 int gap = array.length/2; int j; while(gap > 0){ for (int i = gap; i < array.length; i++) { j = i; //这里的while循环使用的很巧妙吧,相当于是从后往前一个一个交换,最终找到目标要插入的位置 while( j >= gap && array[j-gap] > array[j]){ swap(j-gap,j); j = j-gap; } } gap = gap/2; } } public static void swap(int i, int j){ int temp = 0; temp = array[i]; array[i] = array[j]; array[j] = temp; } public static void main(String[] args) { shellSort(); for (int i = 0; i < array.length; i++) { System.out.println(array[i]); } } }