几种常见的插入排序:
插入排序是一种常见的排序算法,它主要包括直接插入,折半插入和希尔(shell)排序(直接插入排序的改进)等。一组有序数组需要添加一个新的数据后,如何保持仍然有序,则最简单就是遍历数组,找到数据应该插入的位置将其插入即可。
直接插入:
首先,我们将数组分为待排序和已排序两个区间,初始化已排序区间只有一个元素,就是数组中的第一个元素。插入排序算法的核心思想是取待排序区间中的元素,在已排序区间中找合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。
一般插入排序都采用在数组上实现。具体算法描述如下:
a.从第一个元素开始,该元素可以认为已经被排序;
b.取出下一个元素,在已排序的元素序列中从后向前扫描;
c.如果该元素(已排序)大于新元素 ,将该元素移动到下一个位置;
d.重复以上操作c,直到找到已排序的元素小于或者等于新元素的位置;
e.将新元素插入到该位置后;
f.重复步骤b到e
public static void insort(int[] data){
int n=data.length;
if(n<=1){
return ;
}else{
//待排序
for(int i=1;i<n;i++){
int value=data[i];//待排序集合的第一个元素
int j=i-1; //已排序元素的末尾下标
// 找到要插入的位置
for(;j>=0;j--){
if(data[j]>value){
data[j+1]=data[j];
}else{
break;
}
}
data[j+1]=value;
}
}
}
直接插入排序还是冒泡排序好:
时间复杂度相同,都是原地排序算法,但是冒泡排序不管怎末优化,元素交换的次数是一个固定值。插入排序是同样的。但是,从代码实现上看,冒泡排序的数据交换要被插入的数据移动复杂,冒泡排序需要3个赋值操作,而插入排序只需要一个。虽然时间复杂度相同,但是插入排序有很大的优化空间,下面就是对插入排序的优化。
折半插入排序:
折半插入排序,是对插入排序算法的改进。排序思想:有⼀组数据待排序,排序区间data[0]~data[n-1]
将数据分为有序数据和⽆序数据,第⼀次排序时默认data[0]为有序数据,data[1]~data[n-1]为⽆序数据。有序数据分区的第⼀个元素位置为left,最后⼀个元素的位置为right。
遍历⽆序区间的所有元素,每次取⽆序区间的第⼀个元素data[i],因为0~i-1是有序排列的,所以⽤中
点mid将其平分为两部分,然后将待排序数据同中间位置为mid的数据进⾏⽐较。
若待排序数据较⼤,则left–mid-1分区的数据都⽐待排序数据⼩,反之,若待排序数据较⼩,则mid+1–right分区的数据都⽐待排序数据⼤,此时将left或right重新定义为新的合适分区的边界,对新的⼩分区重复上⾯操作。直到left和right 的前后顺序改变,此时right+1所处位置为待排序数据的合适位置。
public static void binaryInSort(int[] data){
int n=data.length;
if(n<=1){
return ;
}else{
for(int i=1;i<n;i++){
int value=data[i];
int left=0;
int right=i-1;
while(left<=right){
int mid=(left+right)/2;
if(data[mid]<value){
left=mid+1;
} else{
right=mid-1;
}
}
//要插入的位置在right+1处,搬移元素
int j=i-1;
for(;j>right;j--){
data[j+1]=data[j];
}
data[j+1]=value;
}
}
}
数组近乎有序时:直接插入排序优于折半插入排序
数组元素倒序时:折半插入排序优于直接差插入排序
希尔排序:
希尔排序是第⼀个突破O(n^2)的排序算法,是简单插⼊排序的改进版。它与插⼊排序的不同之
处在于,它会优先⽐较距离较远的元素。希尔排序⼜叫缩⼩增量排序。
希尔(Shell)排序⼜称缩⼩增量排序,是对直接插⼊排序的优化。
先将整个待排序的记录序列分割成为若⼲⼦序列分别进⾏直接插⼊排序,具体算法描述:
选择⼀个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
按增量序列个数k,对序列进⾏k 趟排序;
每趟排序,根据对应的增量ti,将待排序列分割成若⼲⻓度为m 的⼦序列,分别对各⼦表进⾏直接插⼊排序。仅增量因⼦为1 时,整个序列作为⼀个表来处理,表⻓度即为整个序列的⻓度。
public static void shellSort(int[] data){
int n=data.length;
if(n<=1){
return;
}else{
int step=n/2;
while(step>=1){
for(int i=step;i<n;i++){
int value=data[i];
int j=i-step;
for(;j>=0;j-=step){
if(data[i]<data[j]){
data[j+step]=data[j];
}else{
break;
}
}
data[j+step]=value;
}
step=step/2;
}
}
}
测试代码:
package data.Sort;
public class InSort {
public static void main(String[] args) {
int[] arr = new int[]{9,2,11,3,6,8,9};
insort(arr);
ArrPrint(arr);
binaryInSort(arr);
ArrPrint(arr);
shellSort(arr);
ArrPrint(arr);
}
public static void ArrPrint(int [] data){
for(int i=0;i<data.length;i++){
System.out.print(data[i]+" ");
}
}
}
当数列在近乎有序时,插入排序时间复杂度能达到O(n)的时间复杂度,当数列本身很短,且有序时,插入排序算法的性能特别高。
三种排序算法的比较:
方式 ---------------------时间复杂度 ------- 空间复杂度 ----------稳定性
直接插入排序 --------- O(n^2) --------------- O(1) ------------------ 稳定
折半插入排序 --------- O(n^2)------------------ O(1)----------------- 稳定
希尔排序 ---------------- 不容易计算 ------------O(1) --------------- 不稳定