插入排序与希尔排序讲解
希尔排序是打破了O(n^2)时间复杂度的排序方法,它的理念基础是插入排序
插入排序概念
对同一个数组,我们构思出一个有序数组arr[0],一个无序数组arr[1]开始,无序数组的遍历就是对有序数组的插入元素的筛选
相比较冒泡和选择的交换式排序方式,插入排序的替换方式更加简单,我们来直接看一下代码演示
插入排序代码 1.0版
/** * 插入排序 * 1.我们可以看做有两个数组,分别是有序和无序的 * 有序:arr[0]开始 * 无序:arr[1]开始 * 我们每次从无序的数据中获取一个,插入到有序列表中,只需要判断当 插入的数据 > 有序中的数据就可以了 * 原因:有序数据本身就是有序的 * */public void insertSorting(int[] arr){ int a; int value = arr[1]; //记录需要插入的数据 //第一次排序 for(a=0;a>=0;a--){ if(arr[a] < value){ arr[a+1] = value; break; }else{ arr[a+1] = arr[a]; } } arr[a+1] = value; //第二次排序 value = arr[2]; for(a=1;a>=0;a--){ if(arr[a] < value){ arr[a+1] = value; break; } arr[a+1] = arr[a]; } arr[a+1] = value; //第三次排序 value = arr[3]; for(a=2;a>=0;a--){ if(arr[a] < value){ arr[a+1] = value; break; } arr[a+1] = arr[a]; } arr[a+1] = value; //第N次排序 //......}总结:根据排序规律可以看到 无序元素value = 我们需要插入的元素,来自于arr数组索引1后面的所有 有序元素 a = arr[0],当有无序元素进来时判断,及时发现value < arr[0],value需要前移也只是进行arr[a+1] = arr[a]的操作,直到找到了value的位置 我们才进行了交换
插入排序代码 2.0版
/** * 我们通过查看上述规律,提取最大层的for循环控制需要插入的数arr[b] b = 1开始 * */public void insertSorting_2(int[] arr){ int a; for(int b=1;b<arr.length;b++){ int value = arr[b]; for(a=b-1;a>=0;a--){ if(arr[a] < value){ break; } arr[a+1] = arr[a]; } arr[a+1] = value; }}
插入排序代码 3.0版
/** * 插入排序继续改进 * 我们继续对以上进行改进 * a=b-1(代表了我们需要判断的所有索引),a>=0,arr[a] < value 两个判断依据进行提取 * */public void insertSorting_3(final int[] arr){ for(int b=1;b<arr.length;b++){ int value = arr[b]; int a = b-1; while(a>=0 && arr[a] > value){ arr[a+1] = arr[a]; a--; } arr[a+1] = value; }}
希尔排序概念
希尔排序的理念基础依旧是插入排序,插入排序如果你吃透了的话看希尔排序会非常的简单,你只需要了解一个思想 --- 步值
希尔排序代码(不完整)和插入排序比较
我们引入我们之前的插入排序2.0版本的代码来做个比较
/** * 希尔排序 * 有别于插入排序,他进行拆分比较,并通过步长来进行比较 * */public void xrSorting(int[] arr){ int c = 1; for(int b=c;b<arr.length;b++){ int value = arr[b]; int a; for(a=b-c;a>=0;a--){ if(arr[a] < value){ break; } arr[a+1] = arr[a]; } arr[a+1] = value; }}
/** * 插入排序2.0版本,和上述的希尔排序进行对比 * */public void insertSorting_2(int[] arr){ int a; for(int b=1;b<arr.length;b++){ int value = arr[b]; for(a=b-1;a>=0;a--){ if(arr[a] < value){ break; } arr[a+1] = arr[a]; } arr[a+1] = value; }}区别:我们对比希尔和插入的代码发现,这个希尔是不是写错了?这不是和插入一样的吗?非也,int c是他们的关键区别,也不能说区别,是插入排序的最终变化 当c = 1的时候看起来和插入完全一致是因为插入的无序arr[1]开始其实就是定义了 c = 1,c可以称之为步长,我们可以让c = 2,3,4,5等等例1:[2, 3, 5, 7, 9, 1, 2, 3, 5, 0],我们让C = arr.length/2 = 5,其实就是拆成了[2,1][3,2][5,3][7,5][9,0]五组分别进行插入输出答案[1, 2, 3, 5, 0, 2, 3, 5, 7, 9]拆成的五组分别为[1,2][2,3][3,5][5,7][0,9]接下来的思路,我们继续 c/2 = 2组,步长C = 2,直至C=1,最后做的一次传统的插入排序
例1:/** * 希尔排序 * 有别于插入排序,他进行拆分比较,并通过步长来进行比较 * */public void xrSorting(int[] arr){ int c = arr.length/2; //这里c = 5 for(int b=c;b<arr.length;b++){ int value = arr[b]; int a; for(a=b-c;a>=0;a-=5){ if(arr[a] < value){ break; } arr[a+5] = arr[a]; } arr[a+5] = value; }}
希尔排序代码 1.0版
/** * 希尔排序1.0 * 通过最外层的for循环控制c的变化 * */public void xrSorting(int[] arr){ for(c = arr.length/2;c>=1;c/=2){ for(int b=c;b<arr.length;b++){ int value = arr[b]; int a; for(a=b-c;a>=0;a-=c){ if(arr[a] < value){ break; } arr[a+c] = arr[a]; } arr[a+c] = value; } }}
希尔排序代码 2.0版
/** * 希尔排序2.0 * 和插入排序改进类似,我们提取其中代码进行代码整合 * */public void xrSorting_2(int[] arr){ int c; for(c = arr.length/2;c>=1;c/=2){ for(int b=c;b<arr.length;b++){ int value = arr[b]; int a = b-c; while(a >= 0 && arr[a] > value){ arr[a+c] = arr[a]; a-=c; } arr[a+c] = value; } }}如果我们的c = arr.length / arr.length的话其实本质就是一个原始的插入排序,但是一般情况下我们的时间复杂度要小于O(n^2),对于C的选择要合理