插入排序
插入排序(Insertion Sort)是一种简单直观的排序算法,它的工作原理类似于我们手动排序一手扑克牌。算法的基本思想是将一个数据元素从其输入数组中取出,然后将其插入到已排序的数组中适当的位置,以达到排序的目的。
算法步骤
开始假设:
在开始时,我们假设数组的第一个元素是已排序的。这是我们的初始已排序部分。
选择未排序的元素:
从未排序的部分选择一个元素。在第一次迭代中,这个元素是数组的第二个元素。
找到插入位置:
将这个选定的元素与已排序部分的元素逐一比较,从后往前,找到它应该插入的位置。这类似于我们在扑克牌中找到一张牌应该插入的位置。
移动元素:
一旦找到了插入位置,就将该位置及其后面的所有元素向后移动一位,为新元素腾出空间。
插入元素:
将选定的元素插入到腾出的空间中。
重复过程:
重复步骤2到5,直到整个数组排序完成。
示例
假设我们有一个数组 [4, 3, 2, 10, 12, 1, 5, 6],我们按照插入排序的步骤对其进行排序:
开始时,假设第一个元素 [4] 是已排序的。
选择下一个元素 3,与已排序部分的元素 4 进行比较。因为 3 小于 4,所以 3 应该插入到 4 的前面。移动 4 并插入 3,得到 [3, 4]。
选择下一个元素 2,与已排序部分的元素 [3, 4] 进行比较。2 应该插入到最前面,移动其他元素并插入 2,得到 [2, 3, 4]。
以此类推,直到整个数组排序完成。
性能分析
时间复杂度:插入排序的时间复杂度在最坏情况下(即数组是逆序)是 O(n^2),其中 n 是数组的长度。这是因为对于每个元素,我们可能需要将它与已排序部分的所有元素进行比较,并移动它们。然而,在最好情况下(即数组已经排序或接近有序),插入排序的时间复杂度可以降低到 O(n)。
空间复杂度:插入排序的空间复杂度是 O(1),因为它只需要一个额外的空间来存储当前正在插入的元素。
尽管插入排序在处理大规模数据时可能不是最高效的算法,但由于其实现简单且对于小规模或部分有序的数据集表现良好,因此在某些情况下仍然是一个实用的选择。
以下这个插入排序的版本是从后往前进行比较和插入的。对于数组中的每一个元素,它都会找到该元素在已排序部分中的正确位置,并将其插入。
//// 定义InsertSort函数,参数为一个整数数组a和数组的长度n
void InsertSort(int* a, int n) {
//n为数组数据个数
// 外层循环:遍历数组中的每一个元素(除了最后一个)
for (int i = 0; i < n - 1; i++) {
// 初始化end为当前遍历到的元素的索引
int end = i;
// tmp保存end+1位置(即这一次要插入排序的元素)的值
int tmp = a[end + 1];
// 内层循环:找到tmp应该插入的位置
while (end >= 0) {
// 如果tmp小于当前end位置的元素,则将end位置的元素后移
if (tmp < a[end]) {
a[end + 1] = a[end];
end--; // 继续向前查找插入位置
}
else {
break; // 如果tmp不小于当前元素,则跳出循环
}
}
// 将tmp插入到正确的位置
a[end + 1] = tmp; //这一句可以放到while循环外,也可以放到while循环内的最后一句,
//因为每次比较大小都是用目标元素tmp
//的值(也就是需要进行插入排序的元素)去与它后面的一个元素(也就是a[end])比较,
//由于tmp已保存了目标元素的值,所以目标元素在进行插入排序时并不需要移动位置。
//当要插入的目标元素找到了正确的位置(也就是在end+1这个位置)后,才需要
//移动该元素的位置,通过a[end + 1] = tmp;移动到end+1这个位置。
}
}
使用示例