插入排序法有几种,书中讨论的是 简单插入排序法,也称 直接插入排序法。
这种排序法可以用抽扑克牌的过程类比。
我们将手上的牌和还没抽到的牌分别称为 手牌区 和 新牌区,手牌区始终是有序的。
当抽下一张牌后,会将新牌与手上的牌作比较,插入到合适的地方,使得手牌区维持有序,这也就是插入排序法名字的由来。
当最后一趟排序结束时,所有牌都进入手牌区,排序结束。
由此也可以看出,插入排序法的核心就在于 插入。
实现
考虑抽牌的整个过程,类比数组,可以用自然语言描述如下:
从第二张牌开始,遍历所有的牌:
拿到新牌;
遍历手上所有的牌:
找到新牌的位置;
将新牌插到手上的牌中;
可以用一张简单的图表示插入排序:
C 语言实现
使用 C 语言实现如下:
void INSERTSORT(int cards[], int totalOfCards) {
for (int i = 1; i < totalOfCards; i++) {
int newCard = cards[i];
int j = i - 1;
while (j >= 0 && cards[j] > newCard) {
cards[j + 1] = cards[j];
j--;
}
cards[j + 1] = newCard;
}
}
特性
排序次数
对于 n 个元素的序列,插入排序一共需要 n - 1 趟排序。
空间复杂度
只需要一个辅助空间,即 newCard,故空间复杂度为 O(1)。
比较次数
当数组本身是顺序的,实际只在每趟的边界处进行了比较,所以一共为 n - 1 次;当数组本身是逆序的,那么每次都要比较 i - 1 次(和手上的每张牌都要比较),一共是 次。
时间复杂度
取最好与最坏的平均值约 ,所以插入排序的时间复杂度为 O(
)。
稳定性
因为新牌是按序进行插入的,相同值的新牌也会按顺序插入到受众,所以插入排序法是 稳定性算法。
改良
插入排序的核心是插入,但在插入前需要在手牌中查找要插入的位置,优化插入排序,可以着眼于优化 查找算法。
前文中是通过遍历查找的,在改良算法中,可以使用二分查找来寻找需要插入的位置,这种算法便称为 折半插入排序算法。