基本思想:
把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素进行比较,将它插入到有序表中的适当位置,使之成为新的有序表
即:每一步将一个待排序的数据插入到前面已经排好序的有序序列中,直到插完所有元素为止
图解: 1 将数组分为有序和无序两部分。选取数组的第一个元素作为有序列表的基准值
2 从下一个元素即第i+1个元素开始遍历剩下数组元素,并把当前遍历的元素与基准值(当前元素的前一个)比较,如果比其小说明需要插入。
3 找合适的位置进行插入。首先记录当前位置的元素。然后以当前元素的前一个开始遍历如果小于当前元素的值或者没有到数组索引为0处就一直遍历。并将当前元素的下一个赋予当前元素(腾出位置)直到 找到一个比当前元素大的元素或者已经到数组索引0的位置退出。这样就找到一个合适的位置然后进行插入
4 将记录的元素插入找到的位置
public static void insertSort(int[] arr){
long start=System.currentTimeMillis();
for (int i = 1; i <arr.length ; i++) {
if (arr[i] <arr[i-1]) {//如果当前元素比前一个小
int temp=arr[i];
int j;int k=i ;
for (j=i-1; j >=0 &&temp<arr[j]; j--) {//找当前位置temp的最终插入位置
arr[j+1] = arr[j];//下一个元素值赋予当前元素
k--;
}
// if(k!=i){
// System.arraycopy(arr,k,arr,k+1,i-k);
// arr[k] = temp;
// }
arr[j+1] = temp; //需要插入的值,因为退出for多减了1
}
}
long end= System.currentTimeMillis();
System.out.println(Arrays.toString(arr));
System.out.println("基本插入排序消耗时间:"+(end-start));
}
优缺点
优点:每一次的扫描过程:一边比较,一边挪位置。 挪位置是赋值操作,相比冒泡排序的交换操作,要高效。属于稳定的排序,适合于数据量小,部分数据有序的情况排序。
缺点:比较轮数较多,且每一轮都是不可或缺的,无法进一步进行优化。时间复杂度(n^2)
插入交换排序
当前元素和有序列表的每一个元素进行比较,如果比其小就交换,直到扫描结束为止
效率较差,毕竟频繁交换也消耗性能
//插入交换排序
public static void insertSwapSort(int[] arr){
long start=System.currentTimeMillis();
for (int i = 1; i <arr.length ; i++) {
for (int l = i; l>0&&arr[l]<arr[l-1]; l--) {//此方法lower,因为前面都是有序数据,没必要拿当前位置与前一个进行比较然后交换,所以性能比直接找位置插入差,但毕竟也是一种方法
arr[l]=arr[l]^arr[l-1];
arr[l-1]=arr[l]^arr[l-1];
arr[l]=arr[l]^arr[l-1];
}
}
long end= System.currentTimeMillis();
System.out.println(Arrays.toString(arr));
System.out.println("插入交换排序消耗时间:"+(end-start));
}
折半插入排序
此排序法对基本插入排序的优化与改进
基本思想:
从有序列表寻找合适的位置可以采用二分查找思路,将有序列表分成left=0,和right=当前位置-1的区间。即arr[0,i-1]区间
获取[left,right]区间的中间直middle;如果当前元素值比middle位置元素的值要小。说明查找位置在中间值左边需要缩小区间 right=middle-1;反之需要扩大区间left=middle+1;
重复以上操作直到找到合适位置然后插入
//折半插入排序
public static void doubleInsertSort(int[] arr){
long start=System.currentTimeMillis();
for (int i = 1; i < arr.length; i++) {
int left=0,right = i-1;
int temp=arr[i];
while (left<=right){
int mid=(left+right)>>>1;
if (arr[mid]>temp) {
right=mid-1;
}
else {
left=mid+1;
}
}
if (left!=i){//left本身就是i无需copy
System.arraycopy(arr,left,arr,left+1,i-left);
arr[left]=temp;
}
}
long end= System.currentTimeMillis();
System.out.println(Arrays.toString(arr));
System.out.println("折半插入排序消耗时间:"+(end-start));
}
三种插入排序的效率
依旧是8000个数可以看出折半插入排序效率比其他两种高