
一、直接插入排序
先聊聊直接插入排序的核心思想。这其实很像我们平时整理扑克牌的过程:左手已经排好序的牌,右手拿着待排序的牌,每次从右手抽一张,插到左手合适的位置。
算法上,我们把数组分为两部分:左边是已排序序列,右边是未排序序列。初始时,第一个元素视为有序,其余为无序。接着将右侧元素逐个取出,与左侧有序序列比较,找到合适位置插入。
具体实现时,我们用 end 指针指向有序序列的最后一个元素。取 end + 1 位置的数暂存为 tmp,然后从后往前比较。如果 arr[end] > tmp,说明 tmp 应该插在更前面,于是将 arr[end] 后移一位。重复此过程直到遇到比 tmp 小的数或越界,最后将 tmp 放入空位。
这里有个细节要注意:外层循环控制 end 遍历整个数组,内层循环负责寻找插入位置。当 end 越界时,说明 tmp 是最小值,应放在最前面。
// 直接插入排序
void InsertSort(int* arr, int n) {
for (int i = 0; i < n - 1; i++) {
int end = i;
int tmp = arr[end + 1];
while (end >= 0) {
if (arr[end] > tmp) {
arr[end + 1] = arr[end];
end--;
} else {
break;
}
}
arr[end + 1] = tmp;
}
}
测试一下效果,代码能正确完成升序排列。关于时间复杂度,由于存在两层循环,最坏情况下需要比较和移动 N^2 次,所以复杂度为 O(N^2)。这意味着数据量增大时,性能下降会非常明显。

二、希尔排序
既然直接插入排序在数据量大时效率低,有没有办法优化?答案是肯定的,这就是希尔排序。它的核心思路是'预排序':通过分组让数组整体趋近有序,再进行一次直接插入排序。




