java排序之插入排序(直接插入排序和希尔排序)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java排序之插入排序(直接插入排序和希尔排序)相关的知识,希望对你有一定的参考价值。
上面一片博文探讨了关于的java选择排序(冒泡排序和快速排序)本章将继续探讨java排序之插入排序,插入排序分为直接插入排序和希尔排序两种。
1.直接插入排序思想:在需要排序的一组数据中假设前该数组的前n-1(n >= 2)个数是已经排好序的,现在要把第n个数插入到前面的n-1个数中,使得这n个数也是排好顺序的。如此反复进行,知道n等于需要排序的数组的长度时。就实现了该数组的直接插入排序。
代码如下:
/**
*
* @param a 需要排序的数组
*/
public static void simpleSort(int[] a){
for(int i = 1;i<a.length;i++){//外层for循环从1开始
int temp = a[i];
int j = i-1;
for(;j>=0 && temp < a[j];j--){
a[j+1] = a[j];
}
a[j+1] = temp;
}
}
//代码解释:
例如:需要排序的数组int[] a = {49,38,65,97,76,13,27};
第一次执行该方法执行for循环。
i=1,temp=38,j=0,a[j]= 49 38 < 49 满足for(;j>=0 && temp < a[j];j--)条件
执行a[j+1] = a[j]即a[1] = a[0] 执行后数组为a=[49,49,65,97,76,13,27]
执行j--,执行后j=-1 跳出内部for循环
执行a[j+1] = temp; j=-1 因此 a[0] = 38;
完成第一次循环后 数组a=[38,49,65,97,76,13,27]
第二次执行外部循环
i=2,temp=65,j=1,a[j]=49 65 > 49 不满足内部for循环条件直接跳出
执行 a[j+1] = temp; 即a[2]=65
完成第二次循环后 数组a=[38,49,65,97,76,13,27]
第三次外部循环
i=3,temp=97,j=2,a[j]=65 97 > 65 不满足内部for循环条件直接跳出
执行a[j+1] = temp;即a[3] = 97
完成第三次循环后 数组a=[38,49,65,97,76,13,27]
第四次外部循环
i=4,temp=76,j=3,a[j]=97 76 < 97 满足内部for循环条件
执行a[j+1] = a[j];即a[4] = a[3] 执行后数组为a=[38,49,65,97,97,13,27]
执行j--,执行后j=2 a[j]=65 76 > 65 不满足继续执行内部for循环的条件 直接跳出
执行a[j+1] = temp;即a[3] = 76
完成第四次循环后 数组a=[38,49,65,76,97,13,27]
第五次外部循环
i=5,temp=13,j=4,a[j]=97 13 < 97 满足内部for循环条件
执行a[j+1] = a[j];即a[5] = a[4] 执行后数组为a=[38,49,65,76,97,97,27]
执行j--,执行后j=3,a[j]=76 13 < 76 满足内部for循环条件
执行a[j+1] = a[j];即a[4] = a[3] 执行后数组为a=[38,49,65,76,76,97,27]
执行j--,执行后j=2 a[j]=65 13 < 65 满足内部for循环条件
执行a[j+1]=a[j];即a[3] = a[2] 执行后数组a=[38,49,65,65,76,97,27]
执行j--,执行后j=1 a[j]=49 13 < 49 满足内部for循环条件
执行a[j+1]=a[j];即a[2]=a[1] 执行后数组为a=[38,49,49,65,76,97,27]
执行j--,执行后j=0,a[j] = 38,13 < 38 满足内部for循环条件
执行a[j+1]=a[j];即a[1] = a[0] 执行后数组为a=[38,38,49,65,76,97,27]
执行j--,执行后j=-1,不满足内部for循环条件跳出循环
执行a[j+1] = temp;即a[0]=13 执行后数组a=[13,38,49,65,76,97,27]
完成第五次循环 。
依次循环直到跳出外部for循环整个数组排序完成。
2.希尔排序法
2.1实现思想
在直接插入排序算法中,每次插入一个数。使有序序列 只增加几个节点,而且对插入下一个数没有提供任何帮助。如果比较相隔较远(称为增量)的两个数,使得数移动时能跨过多个数,则进行一次比较就可能消除多个元素交换。D.L.Shell于1959年以他的名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按照某个增量d分成若干组,每组中元素的下标相差d。对每组中全部元素进行排序,然后在用一个较小的分量(较小的分量一般取当前分量的一半d/2)对排序后的的 整个数组进行分组。再对每组进行排序。当增量d=1时,整个要排序的数组本分成一个数组,然后进行排序,排序即可完成。
2.2历史背景
直接插入排序它的效率在某些时候是很高的,比如我们的元素本身就是基本有序的,我们只需要少量的插入操作,就可以完成整个数组的排序工作。此时直接插入很高效。还有就是数据比较少时,直接插入的优势也比较明显。可问题在于两个条件本身过于苛刻,现实中数据少和基本有序都属于特殊情况。有条件当然是好的,条件不存在我们创造条件也要去做,于是科学家D.L.Shell研究出一种排序算法,对直接插入排序改进后可以提高效率。
如何然待排序的数据少呢?很容易想到就是将原本有大量数据的数组进行分组,分割成若干个子序列,此时每个子序列的数据就比较少。然后在这些子序列内分别进行直接插入排序,当整个序列都基本有序时,在对全体记录进行一次直接插入排序。问题其实也就在这里,我们分隔待排序数组的目的就是为了减少数据的量,对每个子序列进行直接插入排序就是为了让整个数组基本有序。
例如数组a=[9,1,5,8,3,7,2,4,6]现在将他按前后顺序分为三组a1=[9,1,5],a2=[8,3,7],
a3=[2,4,6],将三个子序列a1,a2,a3排序后重新组合在一起后a=[1,5,9,3,7,8,2,4,6],此时这个数组还是杂乱无序的,根本谈不上基本有序,要排序还是直接重来一遍插入排序。这样做只是瞎子戴眼镜多余的圈圈,毫无用处,所谓基本有序不是局部有序而是小的数据基本在前面,大的的数据基本在后面,不大不小的数据基本在中间,因此我们会发现将一组需要排序的数据按照先后顺序分组排序后满足不了我们的要求。所以我们需要采取跳跃分割的策略:将相距某个“增量”的数据组成一个子序列,这样才能保证在子序列内分别进行插入排序后得到的结果是一个基本有序的数组而不是一个局部有序的数组。
代码实现:
public static void shellSort(int[] a){
int j;
int len = a.length;
for(int d = len >> 1;d > 0;d = d >> 1 ){
for(int i = d;i<len;i++){
int temp = a[i];
for(j = i; j >= d && temp < a[j-d]; j -= d){
a[j] = a[j-d];
}
a[j] = temp;
}
}
}
代码执行解释:
本文出自 “smartluobo” 博客,请务必保留此出处http://smartluobo.blog.51cto.com/6152293/1745598
以上是关于java排序之插入排序(直接插入排序和希尔排序)的主要内容,如果未能解决你的问题,请参考以下文章