插入排序 (STRAIGHT INSERTION SORT)
原理
插入排序就是每一步将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕。
动态效果示意图
思路
1. 假设我们有一组无序序列a[1]、a[2]、a[3].........a[n]
2. 我们先将这个序列中下标为 0 的元素视为元素个数为 1 的有序序列
3. 然后,我们依次把 a[2].........a[n] 插入到这个有序序列中。所以,我们需要一个外部循环,从下标 1 扫描到 n 。
4. 插入过程。假设我们要把 a[i] 插入到前面的有序序列中。我们需要将 a[i] 和 a[0] ~ a[i-1] 进行比较,以确定需要插入的位置。这就需要一个内部循环,我们一般时从后往前比较,即从小标 i - 1 开始向 0 进行扫描。
- 举例说明
8,3,2,7,4,5
第一趟排序
第一个数 8 看作时一个有序数列,把 3 插进去,3 和 8 作比较,3 小于 8,则 8 向右移动一位,3 插入合适的位置:3,8,2,7,4,5
第二趟排序
将 3 和 8 看作时一个有序数列,要将 2 插进去,2 和 8 比较,2 小于 8,8 向右移动一位;2 和 3 比较,2 比3 小,3 向右移动一位,2 插入合适的位置: 2,3,8,7,4,5
第三趟排序
。。。
。。。
第四趟排序
。。。
。。。
第五趟排序
。。。
。。。
最后的排序结果:2,3, 4, 5, 7, 8
代码实现
public static void insertionSort(int[] arr){
for (int i = 1; i < arr.length; i++) { //从第2个数开始进行插入
int temp = arr[i]; //存放插入元素
int j = 0; //变量j用于保存插入位置
//搜寻插入位置
for(j = i -1; j >= 0; j--){
//从有序区最后最后一个元素开始比较,若比较的元素比插入的元素大,则比较的元素向右移动一位
if(temp < arr[j])
arr[j + 1] = arr[j];
//若比较的元素比插入的元素小,结束比较
if(temp > arr[j])
break;
}
//将插入元素插入到搜寻到的合适位置
arr[j + 1] = temp;
//查看每一步的插入情况
System.out.println(Arrays.toString(arr));
}
}
时间复杂度
当数据时有序时,执行效率最高,每次插入都不用移动前面的元素,时间复杂度为 O(n)。
当数据为反序时,执行效率最差,每次插入都要将前面的所有元素后移,时间复杂度为 O(n^2)。
所以,数据越接近正序,直接插入排序的算法性能越好。
空间复杂度
由直接插入排序算法可知,在排序过程中需要一个临时变量来存储要插入的值,所以空间复杂度为 1 。
算法稳定性
直接插入排序过程中,不改变相等元素的位置,所以它时稳定算法。
优化(1)
二分插入排序算法
因为在一个有序序列中查找要插入的位置,所以可以使用二分查找,减少元素比较次数提高效率。二分查找是对于有序数组而言的,如果数组是升序排序的,二分查找就是不断对数组进行对半分割,每次那中间元素和目标数字做比较,如果中间数字小于目标数字,则说明目标数字在数组的右侧;反之,说明在数组的左侧。
二分插入排序算法:
public static void insertionSort(int[] arr){
for (int i = 1; i < arr.length; i++) { //从第二个数开始进行插入
int temp = arr[i];
//使用二分查找法找到要插入的位置
int l = 0;
int r = i - 1;
while(l <= r){
int m = (l + r) / 2;
if(arr[m] > temp)
r = m - 1;
else
l = m + 1;
}
for(int j = i - 1; j >= l; j--){
arr[j + 1] = arr[j];
}
//插入到适当的位置
arr[l] = temp;
}
}
二分插入排序算法时间复杂度分析:
二分查找最坏时间复杂度:查询次数最多为 x =
log
2
n
\log_2 n
log2n,即O(
log
2
n
\log_2 n
log2n)。所以,二分查找排序比较次数为:x=
l
o
g
2
n
log_2 n
log2n。
二分查找插入排序耗时操作有:比较 + 后移赋值,时间复杂度如下:
优化(2)
希尔排序
原理
对直接插入排序分析得知,其算法的时间复杂度为 O(
n
2
n^2
n2),但是,若待排记录序列为正序时,其时间复杂度可提高到 O(n)。由此可设想,如果待排记录按关键字基本有序,直接插入排序的效率可以大大提高。从另一方面来看,直接插入排序算法简单,所以在 n 值很小时效率比较高。希尔排序正是从这两点出发对直接插入排序改进而得到的一种插入排序算法。
基本思想: 先将整个待排序列分割成若干子序列分别进行直接插入排序,待整个排序中的记录基本有序时,在对全体记录进行一次直接插入排序。先取较大的步长对待排序列进行直接插入排序,每排一次就缩小一次步长,再进行插入排序,最后直到步长变为1。
思路
举例说明
592,401,874,141,348,72,911,887,820,283
============================================================================
第一趟:增量为 5
592,401,874,141,348,72,911,887,820,283
592--------------------------------72
401-------------------------------911
874------------------------------887
141------------------------------820
348--------------------------------283
第一趟排序结果:72,401,874,141,283,592,911,887,820,348
============================================================================
第二趟:增量为 2
第二趟结果:72,141,283,348,820,401,874,592,911,887
============================================================================
第三趟:增量为 1
第三趟排序结果:72,141,283,348,401,592,820,874,887,911
排序完成