基本思想
每一步将一个待排序的数据插入到前面已经排好序的有序序列中,直到插完所有元素为止。
C++代码实现
//插入排序
void insertionSort(vector<int>& v)
{
int tmp = 0;
int i = 1, j = 0; //从1开始比较是将第一个元素是有序的
for (; i < v.size(); i++)
{
tmp = v[i]; //待排序元素
j = i - 1;
for (; j >= 0 && tmp < v[j]; j--)
{
//当前为排序元素比有序序列中的元素小,则有序序列元素后移一位
v[j + 1] = v[j];
}
//当前为排序元素>= 有序序列元素或j < 0时,将未排序元素插入到该元素后面或有序序列头部
v[j + 1] = tmp;
}
}
排序过程
以[2, 2, 5, 1, 3] 为例,黑色加粗的2用来判断算法的稳定性。
第一轮排序,有序序列为[2], 无序序列为[2, 5, 1, 3]。
取无序序列中的第一个元素tmp = v[i] = 2(i = 1)与有序序列中的元素比较。
①j = 0, tmp >= v[j], 不执行循环体退出内层循环
j = -1 < 0, 将无序元素放入有序序列中第一个<= tmp 的元素后面,v[j + 1] = tmp。
第一轮排序结束,得到排序结果,有序序列更新为[2, 2],无序序列更新为[5, 1, 3]。这里可以发现黑色加粗的2依旧在未加粗的2前面。
第二轮排序,tmp = v[i] = 5(i = 2)
①tmp > v[j], 不执行循环体退出内层循环
将当前元素放入有序序列后面,即v[j + 1] = tmp。
第二轮排序结束,得到排序结果,有序序列更新为[2, 2, 5], 无序序列更新为[1, 3]
第三轮排序,tmp = v[i] = 1(i = 3)
①tmp < v[j] = 5, v[j] 后移一位, v[j + 1] = v[j], j-- = 1
②tmp < v[j] = 2, v[j] 后移一位, v[j + 1] = v[j], j-- = 0
③tmp < v[j] = 2, v[j] 后移一位, v[j + 1] = v[j], j-- = -1, 退出内层循环
将当前元素放到有序序列后面,即v[j + 1] = tmp。
第三轮排序结束,得到排序结果,有序序列更新为[1, 2, 2, 5], 无序序列更新为[3]
第四轮排序, tmp = v[i] = 3(i = 4)
①tmp < v[j] = 5, v[j] 后移一位, v[j + 1] = v[j], j-- = 2
②tmp > v[j] = 2, 退出内层循环
将当前元素放到有序序列后面,即v[j + 1] = tmp。
第四轮排序结束,有序序列更新为[1, 2, 2, 3, 5], 无序序列更新为[], 所以无序元素全部插入完毕,完成整体排序。
时间复杂度
可以发现在插入过程中的比较次数在区间[1, n - 1] 内,下面分析这两端的情况。
最好情况:
当待排序数组是有序时,是最优的情况。
只需当前数跟前一个数比较一下就可以了,这时整体排序一共需要比较n - 1次,最好时间复杂度为O(n)。
最坏情况:
是待排序数组是逆序的,此时需要比较次数最多,总次数记为:1+2+3+…+N-1,所以,插入排序最坏情况下的时间复杂度为O(n2)。
综上插入排序的平均复杂度为O(n2)。
因而,插入排序不适合对于数据量比较大的排序应用。
但是,如果需要排序的数据量很小,例如,量级小于千,那么插入排序还是一个不错的选择。
空间复杂度
排序过程中我们只用到3个临时的变量,且不随数据规模n的变化而变化,所以整体空间复杂度为O(1)。
算法稳定性
因为交换元素时,可以在相等的情况下做出不移动的限制,例上面黑色加粗的2和未加粗的2的相对位置并没有发生改变,所以归并排序是稳定的