目录
例子均为从小到大排序
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.个人意见
考的不多。但是要知道这个希尔排序的思想!做了什么?为什么这样做?