插入排序算法
一、介绍
1、使用场景
有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法——插入排序法。
2、基本思想
插入排序的基本思想是:每步将一个待排序的纪录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。
二、常用算法
直接插入、折半插入、希尔排序
说明:
1. 以下方法最后结果均按照升序排列
2. 用4,2,1,3,7,4,5,8,3,5序列进行测试
1、直接插入
(1)基本思想
将待排序元素x与已排序元素从后向前依次比较,找到小于等于x的元素y的位置,将从y到x之间的元素(不包括x和y)依次后移一位,并将x插入到y元素的后面,即完成一趟排序,再用相同的方法继续完成对后面元素的排序操作即可。
(2)代码实现
public void straightInsertionSort(int[] arr){
for(int i=1; i<arr.length; i++){
if (arr[i] < arr[i-1]) { //与前面元素进行比较
int temp = arr[i];
arr[i] = arr[i-1]; //元素后移
int j = i-2;
while (j>=0 && temp < arr[j]) { //找到合适的位置
arr[j+1] = arr[j];
j--;
}
arr[j+1] = temp; //插入到合适的位置
}
print(arr); //每趟结束打印一次数组当前顺序
}
}
(3)运行结果
(4)性能
①稳定性:稳定
②时间复杂度:O(n^2)
2、折半插入排序(二分插入排序)
(1)基本思想
通过折半查找的方式找到待排序元素的合适位置,将相应的元素依次后移,然后再将待排序元素插入,即完成一趟排序操作,再用相同的方式完成对后面元素的排序操作即可。
(2)代码实现
public void binaryInsertionSort(int[] arr){
for (int i = 1; i < arr.length; i++) {
//待排序元素小于前面相邻元素,需要移动位置
if (arr[i] < arr[i-1]) {
int temp = arr[i];
arr[i] = arr[i-1];
//用折半查找的方式找到合适的插入位置
int low = 0;
int height = i-2;
while(low <= height){
//不用(low+height)/2是为了防止low+height的值发生溢出
int mid = low+(height-low)/2;
if (temp < arr[mid]) {
height = mid-1;
}else{
low = mid +1;
}
}
//将元素一次后移一位
for(int j=i-2; j>=height+1; j--){
arr[j+1] = arr[j];
}
arr[height+1] = temp;
}
print(arr);
}
}
(3)运行结果
(4)性能
①稳定性:稳定
②时间复杂度:O(n^2)
3、希尔排序(缩小增量排序)
(1)基本思想
将待排序元素以步长d分成若干组,距离为d的元素在同一组,并在组内进行直接插入排序,所有组都完成一次排序以后,缩小步长,并重复动作,直到步长d=1后完成排序。
这里初始步长设置为数组长度的一般,每完成一次排序后,步长变为原来的一半。
(2)代码实现
public static void ShellSort(int[] arr){
int d = arr.length/2; //步长
while (d >= 1) {
System.out.println("步长为:"+d);
//每趟按照步长d进行直接插入排序
for (int i = d; i < arr.length; i+=d) {
if (arr[i] < arr[i-d]) {
int temp = arr[i];
arr[i] = arr[i-d];
int j = i-2*d;
while(j>=0 && temp < arr[j]){
arr[j+d] = arr[j];
j-=d;
}
arr[j+d] = temp;
}
print(arr);
}
d /= 2;
}
}
(3)运行结果
(4)性能
①稳定性:不稳定
②时间复杂度:希尔排序的时效分析很难,有人在大量的试验基础上推出,当n在某个特定范围内,希尔排序所需的比较次数和移动次数约为n^1.3,比较次数和移动次数还依赖于步长因子序列的选取。目前还没有选取最好的步长因子序列的方法,步长因子有取奇数的,也有取质数的,最后一个步长因子必须是1。