插入排序
排序步骤(有n个数需要排序)
1、从第一个数开始,被认为是有序的
2、取出下一个数,在已经排好数的序列之中插入当前数
3、将之后的数后移
4、重复取数,直到数列取完
解析说明(n个数字需要升序排列)
插入排序的最好情况:序列已经是升序数列,所要进行的比较次数最小,比较次数为n-1次
插入排序的最差情况:序列降序排列,此时需要比较的次数为1+2+3+ ... + (n-1) + n = n(n-1)/2
平均来说插入排序算法时间复杂度为O(N^2)
最优的空间复杂度为开始元素已排序,则空间复杂度为 0;
最差的空间复杂度为开始元素为逆排序,则空间复杂度最坏时为 O(N);
平均的空间复杂度为O(1)
public static int[] insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {//外层循环控制从1开始取数
int temp = arr[i];//当i所在位置的数字向前移动时,arr[i]会被arr[i-1]位置上的数字替代,因此需要有一个变量存储arr[i]上的值
for (int j = i - 1; j >= 0; j--) {//内层循环控制遍历已排序的序列,寻找合适的位置插入arr[i]
if (temp < arr[j]) {
arr[j+1] = arr[j];
}else{//找到合适的位置后退出
break;
}
arr[j] = temp;//插入
}
}
return arr;
}
优化
优化查找效率:采用二分插入排序的方式。通过二分查找不断确定数的范围,直到确定为两个数的中间。时间复杂度降至O(NlogN)
二分查找:在已经排好序的一组数中(这里设定为从小到大),取出最中间的数字 arr[middle] ,比较它与所要查找的数字 arr[i] 的大小。如果 arr[i] < arr[middle] ,那么 arr[i] 所在的位置在0~middle之间;如果 arr[i] < arr[middle] ,那么 arr[i] 所在的位置在middle~n-1之间。以此类推,不断二分,最终能够确定 arr[i] 位于两个数之间
二分查找停止情况分析:程序通过left和right来确认 arr[i] 的位置范围;如果 left < right ,说明此时还未确认 arr[i] 的位置;如果 left > right ,如下图所示,left即为查找的数插入的位置
举个栗子:
//优化一:二分查找
public static int[] insertSort1(int[] arr) {
int left, right, middle, target, t = 0;//target为所需要查找的数,具体作用同temp相同;t记录最终target所在数组的位置的索引
for (int i = 1; i < arr.length; i++) {
target = arr[i];
left = 0;
right = i - 1;
//查找数的位置
while (left <= right) {
middle = (left + right) / 2;
if (target < arr[middle]) {
right = middle - 1;
} else{
left = middle + 1;
}
t = left;
}
//插入数的位置
for (int j = i - 1; j >= t; j--) {//仅堆插入位置之后的数进行位移
//未优化的方法在此处范围为大于等于0是因为还有大小判断限制,能够break跳出循环
arr[j + 1] = arr[j];
}
arr[t] = target;
}
return arr;
}
插入排序代码
import org.junit.Test;
public class InsertSort {
@Test
public void test() {
int[] arr = new int[]{12, 15, 4, 5, 8, 35};
insertSort1(arr);
for (int i : arr) {
System.out.print(i + " ");
}
}
public static int[] insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {//外层循环控制从1开始取数
int temp = arr[i];//当i所在位置的数字向前移动时,arr[i]会被arr[i-1]位置上的数字替代,因此需要有一个变量存储arr[i]上的值
for (int j = i - 1; j >= 0; j--) {//内层循环控制遍历已排序的序列,寻找合适的位置插入arr[i]
if (temp < arr[j]) {
arr[j + 1] = arr[j];
} else {//找到合适的位置后退出
break;
}
arr[j] = temp;//插入
}
}
return arr;
}
//优化一:二分查找
public static int[] insertSort1(int[] arr) {
int left, right, middle, target, t = 0;//target为所需要查找的数,具体作用同temp相同;t记录最终target所在数组的位置的索引
for (int i = 1; i < arr.length; i++) {
target = arr[i];
left = 0;
right = i - 1;
//查找数的位置
while (left <= right) {
middle = (left + right) / 2;
if (target < arr[middle]) {
right = middle - 1;
} else{
left = middle + 1;
}
t = left;
}
//插入数的位置
for (int j = i - 1; j >= t; j--) {//仅堆插入位置之后的数进行位移
//未优化的方法在此处范围为大于等于0是因为还有大小判断限制,能够break跳出循环
arr[j + 1] = arr[j];
}
arr[t] = target;
}
return arr;
}
}