原理
- 把所有的元素分为两组,已经排序的和未排序的。
- 找到未排序的组中的第一个元素,向已经排序的组中进行插入。
- 倒序遍历已经排序的元素,依次和待插入的元素进行比较,直到找到一个元素小于等于待插入元素,那么就把待插入元素排到这个元素后面。
动图演示如下,其中绿色表示已经排序的元素,红色表示未排序元素。
时间复杂度
最好情况:如果待排序数组一开始就是顺序的,那么只要检查一下外层循环,不需要进行内层循环,此时时间复杂度为T = O(n)。
最坏情况:待排序数组一开始就是逆序的,那么每次内循环都要走一遍,此时时间复杂度为T = O(n^2)。
平均:T = O(n^2)。
排序稳定性
只交换比待排序元素大的元素,所以插入排序是稳定的。
优缺点
优点:和冒泡类似,代码实现简单。对于差不多排好序的数组时间复杂度接近O(n)。
缺点:平均时间复杂度达到了O(n^2)。
代码实现
我这里使用的是C#来实现,提供两种版本
下面这个是比较好理解的版本
public static void InsertionSort(ref int[] needSortAarray)
{
// 插入排序外层循环从1开始
for (int i = 1; i < needSortArray.Length; ++i)
{
int insertIndex = i;
// 倒序遍历已经排序好的元素
for (int j = i - 1; j >= 0; --j)
{
// 如果排好序的元素中存在比待插入元素大的情况
// 交换位置
if (needSortArray[insertIndex] < needSortArray[j])
{
Swap(ref needSortArray[insertIndex], ref needSortArray[j]);
insertIndex = j;
}
else
{
// 这步走进来,说明此时待插入元素已经到达正确的位置
// 可以提前退出循环
break;
}
}
}
}
难理解的版本
public static void InsertionSort(ref int[] arr)
{
// 默认第一张牌就是在手中的,所以i从1开始作为arr[i]作为插入元素
for (int i = 1; i < arr.Length; i++)
{
int temp = arr[i];
int j = i;
// 如果插入元素前面的元素都大于待插入的元素,那么都往后移动一位。
for (; j > 0 && arr[j - 1] > temp; j--)
{
arr[j] = arr[j - 1];
}
// 条件不满足时,说明此时j是待插入元素正确的位置。
// 同时存在如果上面循环没有执行的话,此时arr[j]就是arr[i],下面这步是多余的。
arr[j] = temp;
}
}
总结
插入排序和冒泡排序一样也是一种很简单的排序算法,在一些现代编程语言内部库提供的Sort方法中一般是用两种算法组合的方式,n如果数量比较小会使用插入排序,如果n数量比较大时会使用快速排序。