1、描述
待排序的数组分为已排序、未排序两部分;
初始状态时,仅有第一个元素为已排序序列,第一个以外的元素为未排序序列;
此后遍历未排序序列, 将元素逐一插入到已排序的序列中:
即把该为排序元素与原有一排序序列当做一个新序列,
通过一次冒泡排序整合成已排序序列(从右侧开始,两个相邻元素进行比较,
匹配成功则换位置,不成功就不做变动)
例:
源数据 | 3 | 2 | 1 |
---|---|---|---|
步骤1 (3为已排序,2、1 为未排序;3 和 2 比较) | 2 | 3 | 1 |
步骤2.1 (2、3为已排序,1为未排序;3 和 1 比较) | 2 | 1 | 3 |
步骤2.2 (2 和 1 比较) | 1 | 2 | 3 |
2、实现
/*
java.util.DualPivotQuicksort,这个是jdk中的一个类(双轴快速查询类),常见的 Arrays.sort 方法调用的就是这个类的 sort 静态方法;以 int[] 为例,sort 方法主要是用了插入排序
// 排序算法代码均从以下方法中摘取
// private static void sort(int[] a, int left, int right, boolean leftmost)
*/
2.1 基本实现
/**
* 插入排序基本实现
* @param a 需要排序的数组
* @param left 最小下标
* @param right 最大下标
*/
public static void sort(int[] a, int left, int right) {
for (int i = left, j = i; i < right; j = ++i) {
int ai = a[i + 1]; // 记录下标+1的值
// 该循环相当于冒泡排序(局部)
while (ai < a[j]) { // 比较两个下标对应的值,为false时表示0-j下标的值都要比 ai 小
a[j + 1] = a[j]; // 把更大的值赋值给更高下标
if (j-- == left) { // 下标向左移,并做判断避免出现非法下标
break;
}
}
a[j + 1] = ai;
// 注意:无论i、j为何值,0~i / 0~j 区间的数据一定是已排序的
}
}
public class SimpleInsertSort {
// 数组长度
public final static int MAX_SIZE = 10;
public static void main(String[] args) {
int[] arr = new int[MAX_SIZE];
// 数组填充数据
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.valueOf(Math.round(Math.random() * 100) + "");
}
System.out.println("数据:" + Arrays.toString(arr));
sort(arr,0,arr.length-1);
System.out.println("结果:" + Arrays.toString(arr));
}
}
2.2 优化
- 如果起始数据是已排序的,那么可以忽略这一部分;从而减少循环的次数
- 插入排序每次只判断两个值,是否可以扩展为多个值一起比较
/**
* 插入排序优化
* @param a 需要排序的数组
* @param left 最小下标
* @param right 最大下标
*/
public static void sort2(int[] a, int left, int right) {
// 1. 从下标0开始,判断是否存在已排序数据,存在则更新下标(需要进行排序的最小下标)
do {
if (left >= right) { // 避免下标异常
return;
}
} while (a[++left] >= a[left - 1]); // 首次循环:a[1] >= a[0] ...
// 2. 成对插入排序;每执行一次循环,left会对应加2;即每次会对两个值分别进行一次插入排序
// 注:由于一次对两个值排序,需要注意下标的处理,避免越界或者存在遗漏
for (int k = left; ++left <= right; k = right > left+2?++left:left) {
// 每次进入循环体时,会先执行判断语句,所以第一次:k=left,left=left+1
// a1表示较小值,a2表示较大值
int a1 = a[k], a2 = a[left];
if (a1 < a2) { // 比较两下标对应的值,true则交换
a2 = a1; a1 = a[left];
}
// 取值:a[left-2],a[left-1],a[left];后两个是未排序的值,前一个是之前排序好的
while (--k >= 0 && a1 < a[k]) { // 取最大值 赋值到 a[left] 中
a[k + 2] = a[k];
}
a[++k + 1] = a1; // 取中间的值赋值到 a[left-1]
while (--k >= 0 && a2 < a[k]) {
a[k + 1] = a[k];
}
a[k + 1] = a2; // 取最小值赋值到 a[left-2]
}
}
PS. 个人猜测源码中是分这两种情况:1. 起始数据不存在已排序的,直接使用 2.1 的常规插入排序;2. 起始数据存在已排序的,找出已排序的最大下标,以改下标为起始,对数据成对插入排序,对应 2.2
3、结果
数据:[12, 18, 75, 25, 71, 59, 84, 42, 87, 13]
结果:[12, 13, 18, 25, 42, 59, 71, 75, 84, 87]
4、复杂度
最好情况,第二个循环都不需要执行,O(N)
最坏情况,第一个以外的元素都需要和之前的数据做一次交换 O(N*N)